Spring MVC教程——检视阅读(三)

Spring MVC 文件下载

准备下载的文件

image.png

示例:

download.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件下载</title>
</head>
<body>
<h3>SpringMVC文件下载</h3>
<a href="/hellospringmvc/download">下载</a>
</body>
</html>
@Controller
public class DownloadController {

    @RequestMapping("/download")
    public void download(HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException {
        //InputStream inputStream = session.getServletContext().getResourceAsStream("/download/template.gif");
        InputStream inputStream = request.getSession().getServletContext().getResourceAsStream("/download/template.gif");
        //2.输出文件
        //设置响应头
        response.setHeader("Content-Disposition","attachment;filename=template.gif");
        OutputStream outputStream = response.getOutputStream();
        byte[] buff = new byte[1024];
        int lenth = 0;
        while ((lenth= inputStream.read(buff))!= -1){
            outputStream.write(buff,0,lenth);
        }
        //3.关闭资源
        outputStream.close();
        inputStream.close();
    }
}

请求:http://localhost:8080/hellospringmvc/download.jsp

Spring MVC 拦截器

Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录,session是否超时等。

要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。

通常拦截器类可以通过两种方式来定义:

  1. 通过实现HandlerInterceptor接口
  2. 继承HandlerInterceptor接口的实现类(如:HandlerInterceptorAdapter)来定义。

注意:拦截器配置的顺序决定了拦截器的执行顺序,先配置会先被执行!

注意:一个拦截器和多个拦截器的执行顺序看下图。

一个拦截器的执行顺序:
image.png
多个拦截器的执行顺序:
image.png

示例:

@Controller
@RequestMapping("/say")
public class HelloController {
   @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String sayHello() {
        //System.out.println("----------HelloController-----sayHello---------------");
        System.out.println("3.目标控制器-HelloController");
        return "success";
    }
  }
public class FirstInterceptor implements HandlerInterceptor {
    /**
     *preHandle: 在控制器(目标)的方法之前被执行
     *   返回值:控制afterCompletion方法是否被执行
     *       true: 执行afterCompletion
     *       false: 不执行afterCompletion
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("1.FirstInterceptor的preHandle");
        return true;
    }

    /**
     * postHandle: 在控制器(目标)的方法成功执行完成之后(注意:控制器方法失败不执行)
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("5.FirstInterceptor的postHandle");
    }
    /**
     *  afterCompletion: 在执行完前面所有(拦截器和目标)的方法之后执行(注意: 不管控制器方法执行成功与否都会被执行 )
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("7.FirstInterceptor的afterCompletion");
    }
}
public class SecondInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("2.SecondInterceptor的preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("4.SecondInterceptor的postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("6.SecondInterceptor的afterCompletion");
    }
}

hellospringmvc-servlet.xml

<!--配置拦截器 :拦截器配置的顺序决定了拦截器的执行顺序,先配置会先被执行!-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--要拦截请求路径-->
        <mvc:mapping path="/**/*"/>
        <bean class="com.self.interceptor.FirstInterceptor"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/say/hello"/>
        <!--如果只这么写就是只拦截路径为/hello的,上面的/say/hello是不会被拦截的-->
        <mvc:mapping path="/hello"/>
        <bean class="com.self.interceptor.SecondInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

请求:http://localhost:8080/hellospringmvc/say/hello

输出:

1.FirstInterceptor的preHandle
2.SecondInterceptor的preHandle
3.目标控制器-HelloController
4.SecondInterceptor的postHandle
5.FirstInterceptor的postHandle
6.SecondInterceptor的afterCompletion
7.FirstInterceptor的afterCompletion

登录超时拦截器:

public class RequestSessionTimeOutInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try{
            String uri = request.getRequestURI();
            if(uri == null){
                return true;
            }
             HttpSession session = request.getSession();
            if (null == session) {
                //401:HTTP401错误代表用户没有访问权限,需要进行身份认证
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                return false;
            }
            return true;
        } catch (Exception e){
            //异常情况不拦截
            logger.error("拦截器配置失败",e);
            return true;
        }
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

Spring MVC 异常处理机制

在控制器的方法发生异常后,默认情况会显示Tomcat的500页面,这种用户体验并不好。如果我们在每个控制器方法自行捕获异常,显然又太繁琐。有没有好的异常处理办法呢?有的,就是Spring MVC的全局异常处理机制。Spring MVC提供了两种全局异常处理机制:

  1. 定义异常类,实现HandlerExceptionResolver接口
  2. 定义异常类,使用@ControllerAdvice+@ExceptionHandler注解

编写全局异常处理类

全局异常类编写方式一

实现HandlerExceptionResolver接口,然后实现resolveException方法,编写处理异常逻辑。

示例:

@Controller
@RequestMapping("/say")
public class HelloController {

    @RequestMapping(value = "/error")
    public String error() {
        int i = 100/0;
        return "success";
    }
  }
public class SimpleHandlerExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView mv = new ModelAndView("error");
        mv.addObject("errorMsg",ex.getMessage());
        return mv;
    }
}

hellospringmvc-servlet.xml

<!--创建自定义异常处理对象-->
<bean class="com.self.exceptionhandler.SimpleHandlerExceptionResolver"/>
全局异常类编写方式二

直接在类上使用@ControllerAdvice,在异常处理方法上添加@ExceptionHandler注解。这种做法底层是AOP思想。

示例:

@ControllerAdvice
public class SimpleExceptionHandler {

    @ExceptionHandler
    public ModelAndView handlerException(Exception e){
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        mv.addObject("errorMsg",e.getMessage());
        return mv;
    }
}
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    @ResponseBody
    Response handleException(Exception exception){
        log.error("系统异常信息:{}",exception);
        return Response.fail(null,"系统异常,请稍后重试或联系管理员!");
    }

    @ExceptionHandler({BusinessException.class})
    @ResponseBody
    Response handleBusinessException(BusinessException exception){
        log.error("业务异常信息为:{}",exception);
        return new Response<>(exception.getCode() != null ? Integer.parseInt(exception.getCode()) : ResultCode.FAIL.getCode(),exception.getMessage(),null);
    }

    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    @ResponseBody
    Response handleTypeMismatchException(MethodArgumentTypeMismatchException exception){
        log.error("业务异常信息为:{}",exception);
        return Response.fail(null,"参数有误,请检查参数信息!");
    }

    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    @ResponseBody
    public Object validationExceptionHandler(MethodArgumentNotValidException exception) {
        StringBuffer errorInfo = new StringBuffer();
        for(ObjectError error :exception.getBindingResult().getAllErrors()){
            errorInfo.append(String.format(",%s",error.getDefaultMessage()));
        }
        log.error("参数验证异常信息为:{}",errorInfo.toString());
        return Response.fail(null,errorInfo.toString().substring(1,errorInfo.toString().length()));
    }

}

hellospringmvc-servlet.xml

<!--创建自定义异常处理对象-->
<bean class="com.self.exceptionhandler.SimpleExceptionHandler"/>

请求:http://localhost:8080/hellospringmvc/say/error

Spring MVC 异常处理机制没处理前:

HTTP Status 500 - Request processing failed; nested exception is java.lang.ArithmeticException: / by zero

处理后:

image.png

注意:如果两种都配置了,会被面向切面先执行返回了。类上使用@ControllerAdvice,在异常处理方法上添加@ExceptionHandler注解。这种做法底层是AOP思想。

Spring MVC 表单数据验证

Spring MVC提供了表单数据验证功能 。

前提:导入数据验证依赖包。

表单数据验证的重点是在Pojo类使用相关注解指定每个属性的验证规则。以下为可以使用的注解:

注解名称 说明
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内(长度大小)
@Digits(integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式

在Controller中,我们需要判断Pojo是否产生了验证错误信息,如果有的话,则把信息转给JSP页面显示。

示例:

pom.xml

<!-- 验证器所需的包 -->
<dependency>
  <groupId>com.fasterxml</groupId>
  <artifactId>classmate</artifactId>
  <version>1.4.0</version>
</dependency>

<dependency>
  <groupId>org.jboss.logging</groupId>
  <artifactId>jboss-logging</artifactId>
  <version>3.3.2.Final</version>
</dependency>

<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.0.13.Final</version>
</dependency>

<dependency>
  <groupId>javax.validation</groupId>
  <artifactId>validation-api</artifactId>
  <version>2.0.1.Final</version>
</dependency>
public class User {
    private Integer id;
    @NotNull
    @Pattern(regexp = "^([a-zA-Z]*[0-9_-]*$)", message = "只能包含字母、数字、下划线,且不能以数字或下划线开头")
    @Size(min = 1, max = 110)
    private String name;
    @NotNull
    @Range(min = 1,max = 100,message = "年龄不在合法范围内")
    private Integer age;
    @Pattern(regexp = "^([a-zA-Z]*$)", message = "只能包含字母")
    private String ride;
    //...
    }
@Controller
public class ValidateController {

    @RequestMapping("/check")
    public String check(@Valid User user, BindingResult result, Model model) {
        //如果表单数据验证有异常
        if (result.hasErrors()) {
            //取出所有失败信息
            List<FieldError> fieldErrors = result.getFieldErrors();
            for (FieldError fieldError : fieldErrors) {
                //把错误信息存入request域,传递到JSP页面显示
                model.addAttribute("ERROR_" + fieldError.getField(), fieldError.getDefaultMessage());
            }
            return "forward:validate.jsp";
        }

        System.out.println("User=" + JSON.toJSONString(user));
        return "success";
    }
}

validate.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>表单数据验证</title>

</head>
<body>
<form action="/hellospringmvc/check" method="post">
    用户名:<input type="text" name="name">${ERROR_name}<br/>
    年龄:<input type="text" name="age">${ERROR_age}<br/>
    坐骑:<input type="text" name="ride">${ERROR_ride}<br/>
    <input type="submit" value="提交">
</form>
</body>
</html>

请求:<http://localhost:8080/hellospringmvc/validate.jsp>

输出:

image.png

Maven单模块SSM整合

本文讲解使用Maven单模块方式进行Spring MVC+Spring+MyBatis整合。为了把整合步骤体现地更加清晰,我们可以把步骤分为以下六个部分:

  1. 准备数据库环境
  2. 单独搭建Spring环境
  3. 单独搭建Spring MVC环境
  4. Spring整合Spring MVC
  5. 单独搭建MyBatis环境
  6. MyBatis整合Spring

1、准备数据库环境

image.png
CREATE TABLE `t_user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(64) NOT NULL COMMENT '姓名',
  `dept` varchar(254) NOT NULL COMMENT '部门',
  `phone` varchar(16) NOT NULL COMMENT '电话',
  `height` decimal(10,2) DEFAULT NULL COMMENT '身高',
  `create_emp` bigint(20) NOT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `modify_emp` bigint(20) DEFAULT NULL COMMENT '修改人',
  `modify_time` datetime DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='用户表';

2、单独搭建Spring环境

创建Web项目——使用原来的hellospringmvc
SSM相关依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.self</groupId>
  <artifactId>hellospringmvc</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <!--
  SSM整合的基础依赖
-->
  <!-- 1.spring相关的依赖 -->
  <dependencies>
    <!-- 1.1 ioc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <!--1.2 aop -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.7</version>
    </dependency>
    <!-- 1.3 声明式事务-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <!-- 1.4 test -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>


    <!-- 2. mybatis相关依赖 -->
    <!-- 2.1 mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.46</version>
    </dependency>
    <!-- 2.2 数据源 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.6</version>
    </dependency>
    <!-- 2.3 mybatis核心包 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
    </dependency>
    <!-- 3. springmvc相关依赖-->
    <!-- 3.1 springmvc核心包 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <!--3.2 servlet依赖 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>provided</scope>
    </dependency>
    <!--3.3 jstl标签库-->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <!-- 4. log4j日志 -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.2</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.3</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>1.2.3</version>
    </dependency>

    <!-- 5. spring与mybatis整合包 *** -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.29</version>
    </dependency>
    <!-- jackson支持包 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.5</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.5</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.5</version>
    </dependency>

    <!-- 验证器所需的包 -->
    <dependency>
      <groupId>com.fasterxml</groupId>
      <artifactId>classmate</artifactId>
      <version>1.4.0</version>
    </dependency>

    <dependency>
      <groupId>org.jboss.logging</groupId>
      <artifactId>jboss-logging</artifactId>
      <version>3.3.2.Final</version>
    </dependency>

    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.13.Final</version>
    </dependency>

    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>2.0.1.Final</version>
    </dependency>
  </dependencies>
</project>
设计Pojo
public class User {

    /**
     * id
     */
    private int id;
    /**
     * 名字
     */
    private String name;
    /**
     * 部门,帝国
     */
    private String dept;
    /**
     * 联系号码
     */
    private String phone;
    /**
     * 身高
     */
    private BigDecimal height;
    /**
     * 创建人
     */
    private Long createEmp;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 修改人
     */
    private Long modifyEmp;
    /**
     * 修改时间
     */
    private Date modifyTime;
    //...
    }
编写业务接口和实现
public interface UserService {

    List<User> getALlUsers();
}
//给业务实现类加入@Service注解,目的是把该对象放入Spring IOC容器。
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
    @Override
    public List<User> getALlUsers() {
        logger.error("查询所有用户成员...");
        return userMapper.getALlUsers();
    }
}
编写Spring配置

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/tx
             http://www.springframework.org/schema/tx/spring-tx.xsd
              http://www.springframework.org/schema/aop
              http://www.springframework.org/schema/aop/spring-aop.xsd
                http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
    <!--spring 容器扫描配置-->
    <context:component-scan base-package="com.self">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>
Spring环境单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTest {

    @Autowired
    private UserService userService;

    @Test
    public void test() {
        userService.getALlUsers();
    }

}

输出表示spring环境搭建成功:

查询所有用户成员...

3、 单独搭建Spring MVC环境

Spring MVC核心控制器web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://java.sun.com/xml/ns/javaee"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
       version="2.5">
   <!-- 配置核心控制器 :DispatcherServlet -->
   <servlet>
      <servlet-name>hellospringmvc</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!-- springmvc配置文件加载路径
             1)默认情况下,读取WEB-INF下面的default-servlet.xml文件
             2)可以改为加载类路径下(resources目录),加上classpath:
         -->
      <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>WEB-INF/hellospringmvc-servlet.xml</param-value>
         <!--<param-value>WEB-INF/simple-servlet.xml</param-value>-->
      </init-param>
      <!--init-param必须放在load-on-startup前,否则会报错:invalid content was found starting with element 'init-param'. One of '{"http://java.sun.com/xml/ns/javaee":run-as, "http://java.sun.com/xml/ns/javaee":security-role-ref}' is expected-->
      <!--
           DispatcherServlet对象创建时间问题
              1)默认情况下,第一次访问该Servlet时创建对象,默认是访问时创建,意味着在这个时间才去加载hellospringmvc-servlet.xml
              2)可以改变为在项目启动时候就创建该Servlet,提高用户访问体验。
                  <load-on-startup>1</load-on-startup>
                        数值越大,对象创建优先级越低! (数值越低,越先创建)
        -->
      <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
      <servlet-name>hellospringmvc</servlet-name>
      <url-pattern>/</url-pattern>
   </servlet-mapping>
   <!--重新配置Tomcat的DefaultServlet的映射路径-->
   <servlet-mapping>
      <servlet-name>default</servlet-name>
      <url-pattern>*.html</url-pattern>
      <url-pattern>*.jpg</url-pattern>
      <url-pattern>*.css</url-pattern>
      <url-pattern>*.js</url-pattern>
      <url-pattern>*.png</url-pattern>
   </servlet-mapping>
</web-app>
springmvc配置——hellospringmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 1.扫描Controller的包-->
    <context:component-scan base-package="com.self" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!-- 2.配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 2.1 页面前缀 -->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <!-- 2.2 页面后缀 -->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3.开启mvc注解驱动-->
    <mvc:annotation-driven/>
</beans>
编写UserController
@RequestMapping("/user")
@Controller
public class UserController {
    /**
     * 查询所有用户
     */
    @RequestMapping("list")
    public String showAll(Model model) {
        List<User> users = userService.getALlUsers();
        //存入数据到request域
        model.addAttribute("list","用户数据");
        //返回list.jsp页面
        return "userList";
    }
}

userList.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>显示用户数据</title>
</head>
<body>
${list}
</body>
</html>
项目部署到Tomcat

请求:<http://localhost:8080/user/list>

image.png

4 、Spring整合Spring MVC

配置Spring监听器

Spring和Spring MVC融合使用,只要在web.xml配置监听器,在项目启动的时候,加载applicationContext.xml文件,把Spring环境启动即可。

web.xml

<!-- 配置spring监听器,用于加载applicationContext.xml(初始化SpringIOC容器) -->
<context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath*:applicationContext.xml</param-value>
</context-param>
<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
在控制层调用业务层,如果Controller成功注入Service,代表Spring与Spring MVC整合成功!
@RequestMapping("/user")
@Controller
public class UserController {

    @Autowired
    private UserService userService;
    /**
     * 查询所有用户
     */
    @RequestMapping("list")
    public String showAll(Model model) {
        List<User> users = userService.getALlUsers();
        //存入数据到request域
        model.addAttribute("list","用户数据");
        //返回list.jsp页面
        return "userList";
    }
}

输出:

image.png

5、 单独搭建MyBatis环境

编写UserDao接口
@Repository
public interface UserMapper {

    List<User> getALlUsers();
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.self.dao.UserMapper">

    <select id="getALlUsers" resultType="User">
        select u.create_time createTime,u.id id ,u.name name ,u.dept dept,u.phone phone from `t_user` u where 1 = 1
    </select>
</mapper>

mybatis-config.xml——该文件是MyBatis核心配置,里面配置数据源及Dao接口映射等信息。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 读取jdbc.properties -->
    <properties resource="jdbc.properties"/>

    <!--1.别名扫描 -->
    <typeAliases>
        <package name="com.self.pojo"/>
    </typeAliases>

    <!--2.数据库连接 -->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="jdbc"></transactionManager>
            <dataSource type="pooled">
                <property name="url" value="${jdbc.url}"/>
                <property name="driver" value="${jdbc.driver}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--3.映射关联 -->
    <mappers>
        <package name="com.self.dao"/>
    </mappers>

</configuration>

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/hello_mybatis?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
jdbc.initialSize=3
jdbc.maxActive=10
MyBatis测试类
public class MabatisTest {

    @Test
    public void testFindAll() throws IOException {

        //1.加载SqlMapConfig.xml
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

        //2.创建SqlSessionFactory
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);

        //3.创建SqlSession
        SqlSession sqlSession = factory.openSession();

        //4.创建Dao代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //5.执行方法
        List<User> list = userMapper.getALlUsers();
        System.out.println(JSON.toJSONString(list));
        //6.释放资源
        sqlSession.close();
        in.close();

    }
}

输出:

[{"createTime":1585635455000,"dept":"amy empire","id":1,"name":"大青山","phone":"123456"},{"createTime":1585635455000,"dept":"amy empire","id":2,"name":"艾米哈珀","phone":"123456"},{"createTime":1585635455000,"dept":"amy empire","id":3,"name":"池寒枫","phone":"123456"},{"createTime":1585647970000,"dept":"森林矮人王国","id":4,"name":"霍恩斯","phone":"852-253521"}]

参考代码位置:

image.png

6 、MyBatis整合Spring

整合的思路是:Spring依赖IOC容器创建MyBatis所需要的SqlSessionFactory,从而利用SqlSessionFactory完成Dao层的操作。

注意:

因为Spring已经把之前MyBatis的数据源及Dao映射等信息都集成了,所以MyBatis的mybatis-config.xml已经不需要啦,可以删除。

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/tx
             http://www.springframework.org/schema/tx/spring-tx.xsd
              http://www.springframework.org/schema/aop
              http://www.springframework.org/schema/aop/spring-aop.xsd
                http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
    <!--spring 容器扫描配置-->
    <context:component-scan base-package="com.self">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--载入properties-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 1. 创建数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 2. 为了创建Dao代理对象,先创建SqlSessionFactory对象 -->
    <!--  SqlSessionFactoryBean: 创建SqlSessionFactory对象的工具 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!--typeAliasesPackage:批量别名处理 通过这些property就可以把mybatis-config.xml替代掉了-->
        <property name="typeAliasesPackage" value="com.self.pojo"/>
        <!-- 所有配置的mapper文件 该配置相当于是mybatis-config.xml里的mappers配置,在这边直接扫描获取了-->
        <!--<property name="mapperLocations" value="classpath*:com/self/dao/*.xml"/>-->
    </bean>

    <!-- 3. 扫描Dao接口所在包,扫描后用于创建Dao代理对象,把代理对象放入IOC容器 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- Dao扫描目录 -->
        <property name="basePackage" value="com.self.dao"/>
        <!--<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>-->
    </bean>
    <!-- 添加事务支持 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 注册事务管理驱动 表示支持声明式事务 @Transactional 注解标注的会被代理实现事务,但要用在有接口的public方法中-->
    <!--基于注解的方式使用事务配置声明-->
    <tx:annotation-driven transaction-manager="transactionManager" />
</beans>
业务层注入持久层对象
//给业务实现类加入@Service注解,目的是把该对象放入Spring IOC容器。
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
    @Override
    public List<User> getALlUsers() {
        logger.error("查询所有用户成员...");
        return userMapper.getALlUsers();
    }
}
编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext.xml")
public class SpringMyBatisTest {

    //从IOC容器中获取业务实现
    @Autowired
    private UserService userService;

    @Test
    public void testFindAll(){
        System.out.println(JSON.toJSONString(userService.getALlUsers()));
    }
}

SSM框架已经整合完成。剩下就是把数据显示到JSP页面上。

修改UserController类
@RequestMapping("/user")
@Controller
public class UserController {

    @Autowired
    private UserService userService;
    /**
     * 查询所有用户
     */
    @RequestMapping("list")
    public String showAll(Model model) {
        List<User> users = userService.getALlUsers();
        //存入数据到request域
        model.addAttribute("list", users);
        //model.addAttribute("list","用户数据");
        //返回list.jsp页面
        return "userList";
    }
}
修改JSP页面显示内容

userList.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>显示用户数据</title>
</head>
<body>
<h3>用户列表</h3>
<table border="1">
    <tr>
        <td>编号</td>
        <td>用户名</td>
        <td>帝国</td>
        <td>电话号码</td>
        <td>创建时间</td>
    </tr>
    <!--
    items: 需要需要遍历的集合
    var: 每个对象的变量名称
    -->
    <c:forEach items="${list}" var="user">
        <tr>
            <td>${user.id}</td>
            <td>${user.name}</td>
            <td>${user.dept}</td>
            <td>${user.phone}</td>
            <td>${user.createTime}</td>
        </tr>
    </c:forEach>
</table>
</body>
</html>

输出:

image.png
报错:

1、在通过controller请求到底层service时报错:springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.self.service.UserService'。

A:这种情况下一般是UserService没有注册到spring容器中,一般分两种情况,要么扫描bean时没有扫到,所以没添加,要嘛是UserService实现类(注意不是UserService接口)没有配置@Service,所以spring没有把他当成一个组件注册到容器中。还有就是在配置UserService实现类时配置@Service错误,要指定名称,否则默认是类名首字母小写后的全称,因此会找不到bean。

还有就是web.xml缺少ContextLoaderListener配置,导致spring容器里的bean没有被加载。

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

2、启动项目时无法启动成功,userService注入不了dao依赖。

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userMapper'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userMapper' defined in file [F:\practice\hellospringmvc\target\hellospringmvc-1.0-SNAPSHOT\WEB-INF\classes\com\self\dao\UserMapper.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'User'.  Cause: java.lang.ClassNotFoundException: Cannot find class: User
 <!--A:这是由于UserMapper.xml的resultType="User",没有指定全路径,这时候要嘛指定全路径,要嘛在applicationContext.xml配置sqlSessionFactory时批量别名处理。
如下: -->
    <!-- 2. 为了创建Dao代理对象,先创建SqlSessionFactory对象 -->
    <!--  SqlSessionFactoryBean: 创建SqlSessionFactory对象的工具 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!--typeAliasesPackage:批量别名处理 通过这些property就可以把mybatis-config.xml替代掉了-->
        <property name="typeAliasesPackage" value="com.self.pojo"/>
    </bean>

Maven多模块SSM整合

一些中大型项目,我希望采用Maven多模块构建方式来搭建SSM整合项目。

Maven多模块构建SSH项目架构图:

img

示例:

新建一个noodle-parent的project工程。子项目通过右击项目名创建新的maven依赖模块。

image.png
image.png

整体结构:

image.png
image.png

1、建立parent工程

配置父工程noodle-parent——pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.self</groupId>
    <artifactId>noodle-parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>noodle-pojo</module>
        <module>noodle-dao</module>
        <module>noodle-service</module>
        <module>noodle-web</module>
    </modules>

    <!--
SSM整合的基础依赖
-->
    <!-- 1.spring相关的依赖 -->
    <dependencies>
        <!-- 1.1 ioc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--1.2 aop -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
        <!-- 1.3 声明式事务-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- 1.4 test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>


        <!-- 2. mybatis相关依赖 -->
        <!-- 2.1 mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <!-- 2.2 数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.6</version>
        </dependency>
        <!-- 2.3 mybatis核心包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <!-- 3. springmvc相关依赖-->
        <!-- 3.1 springmvc核心包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--3.2 servlet依赖 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <!--3.3 jstl标签库-->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- 4. log4j日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!-- 5. spring与mybatis整合包 *** -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>
    </dependencies>
</project>

2、建立pojo工程

编写Pojo类
public class User {

    /**
     * id
     */
    private int id;
    /**
     * 名字
     */
    private String name;
    /**
     * 部门,帝国
     */
    private String dept;
    /**
     * 联系号码
     */
    private String phone;
    /**
     * 身高
     */
    private BigDecimal height;
    /**
     * 创建人
     */
    private Long createEmp;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 修改人
     */
    private Long modifyEmp;
    /**
     * 修改时间
     */
    private Date modifyTime;
    //...
    }

3、建立dao工程

依赖domain工程
<dependencies>
    <dependency>
        <groupId>com.self</groupId>
        <artifactId>noodle-pojo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
public interface UserMapper {

    List<User> getALlUsers();
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.self.dao.UserMapper">

    <select id="getALlUsers" resultType="User">
        select u.create_time createTime,u.id id ,u.name name ,u.dept dept,u.phone phone from `t_user` u where 1 = 1
    </select>
</mapper>
编写Spring的Dao配置

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/hello_mybatis?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456

applicationContext-dao.xml文件只存放与Dao有关的配置 。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/tx
             http://www.springframework.org/schema/tx/spring-tx.xsd
              http://www.springframework.org/schema/aop
              http://www.springframework.org/schema/aop/spring-aop.xsd
                http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
    <!--载入properties-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 1. 创建数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 2. 为了创建Dao代理对象,先创建SqlSessionFactory对象 -->
    <!--  SqlSessionFactoryBean: 创建SqlSessionFactory对象的工具 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!--typeAliasesPackage:批量别名处理 通过这些property就可以把mybatis-config.xml替代掉了-->
        <property name="typeAliasesPackage" value="com.self.pojo"/>
        <!-- 所有配置的mapper文件 该配置相当于是mybatis-config.xml里的mappers配置,在这边直接扫描获取了-->
        <!--<property name="mapperLocations" value="classpath*:com/self/dao/*.xml"/>-->
    </bean>

    <!-- 3. 扫描Dao接口所在包,扫描后用于创建Dao代理对象,把代理对象放入IOC容器 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- Dao扫描目录 -->
        <property name="basePackage" value="com.self.dao"/>
        <!--<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>-->
    </bean>
</beans>

4、建立Service工程

依赖Dao工程 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>noodle-parent</artifactId>
        <groupId>com.self</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>noodle-service</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.self</groupId>
            <artifactId>noodle-dao</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
public interface UserService {

    List<User> getALlUsers();
}
//给业务实现类加入@Service注解,目的是把该对象放入Spring IOC容器。
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public List<User> getALlUsers() {
        System.out.println("查询所有用户成员...");
        return userMapper.getALlUsers();
    }
}
编写Spring的Service配置

applicationContext-service.xml

该配置主要需要扫描Service实现类和配置Spring声明式事务。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/tx
             http://www.springframework.org/schema/tx/spring-tx.xsd
              http://www.springframework.org/schema/aop
              http://www.springframework.org/schema/aop/spring-aop.xsd
                http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
    <!--spring 容器扫描配置-->
    <context:component-scan base-package="com.self">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--Spring声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="load*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="select*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--事务切面-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="pt" expression="execution(* com.self.service.impl.*ServiceImpl.*(..))"/>
        <!--切面-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
    </aop:config>
</beans>

5、建立Web工程——项目为Web项目

依赖Service工程 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>noodle-parent</artifactId>
        <groupId>com.self</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>war</packaging>

    <artifactId>noodle-web</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.self</groupId>
            <artifactId>noodle-service</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
配置监听器和核心控制器

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://java.sun.com/xml/ns/javaee"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
       version="2.5">
   <!-- 配置核心控制器 :DispatcherServlet -->
   <servlet>
      <servlet-name>dispatcherServlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!-- springmvc配置文件加载路径
             1)默认情况下,读取WEB-INF下面的default-servlet.xml文件
             2)可以改为加载类路径下(resources目录),加上classpath:
         -->
      <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath:noodlespringmvc-servlet.xml</param-value>
         <!--<param-value>WEB-INF/simple-servlet.xml</param-value>-->
      </init-param>
      <!--init-param必须放在load-on-startup前,否则会报错:invalid content was found starting with element 'init-param'. One of '{"http://java.sun.com/xml/ns/javaee":run-as, "http://java.sun.com/xml/ns/javaee":security-role-ref}' is expected-->
      <!--
           DispatcherServlet对象创建时间问题
              1)默认情况下,第一次访问该Servlet时创建对象,默认是访问时创建,意味着在这个时间才去加载hellospringmvc-servlet.xml
              2)可以改变为在项目启动时候就创建该Servlet,提高用户访问体验。
                  <load-on-startup>1</load-on-startup>
                        数值越大,对象创建优先级越低! (数值越低,越先创建)
        -->
      <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
      <servlet-name>dispatcherServlet</servlet-name>
      <url-pattern>/</url-pattern>
   </servlet-mapping>
   <!--重新配置Tomcat的DefaultServlet的映射路径-->
   <servlet-mapping>
      <servlet-name>default</servlet-name>
      <url-pattern>*.html</url-pattern>
      <url-pattern>*.jpg</url-pattern>
      <url-pattern>*.css</url-pattern>
      <url-pattern>*.js</url-pattern>
      <url-pattern>*.png</url-pattern>
   </servlet-mapping>

   <!-- 配置spring监听器,用于加载applicationContext.xml(初始化SpringIOC容器) -->
   <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:applicationContext-*.xml</param-value>
   </context-param>
   <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>

   <!-- 字符编码过滤器 -->
   <filter>
      <filter-name>encodingFilter</filter-name>
      <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
      <init-param>
         <param-name>encoding</param-name>
         <param-value>utf-8</param-value>
      </init-param>
   </filter>
   <filter-mapping>
      <filter-name>encodingFilter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

注意:

Spring监听器读取的路径为classpath*:,这个语法指加载当前项目及依赖工程的所有符合条件的文件。因为applicationContext.xml分布在不同的Maven工程,所以必须使用该语法加载!

noodlespringmvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 1.扫描Controller的包-->
    <context:component-scan base-package="com.self" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!-- 2.配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 2.1 页面前缀 -->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <!-- 2.2 页面后缀 -->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3.开启mvc注解驱动-->
    <!--不添加也能使用,高版本spring已经默认实现了。
在Spring中一般采用@RequestMapping注解来完成映射关系,要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例,这两个实例分别在类级别和方法级别处理。而<mvc:annotation-driven/>配置帮助我们自动完成上述两个实例的注入。
    -->
    <mvc:annotation-driven/>
    <!--<mvc:default-servlet-handler/>-->
</beans>
@RequestMapping("/user")
@Controller
public class UserController {

    @Autowired
    private UserService userService;
    /**
     * 查询所有用户
     */
    @RequestMapping("/list")
    public String showAll(Model model) {
        List<User> users = userService.getALlUsers();
        //存入数据到request域
        model.addAttribute("list", users);
        //model.addAttribute("list","用户数据");
        //返回list.jsp页面
        return "userList";
    }
}

userList.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>显示用户数据</title>
</head>
<body>
<h3>用户列表</h3>
<table border="1">
    <tr>
        <td>编号</td>
        <td>用户名</td>
        <td>帝国</td>
        <td>电话号码</td>
        <td>创建时间</td>
    </tr>
    <!--
    items: 需要需要遍历的集合
    var: 每个对象的变量名称
    -->
    <c:forEach items="${list}" var="user">
        <tr>
            <td>${user.id}</td>
            <td>${user.name}</td>
            <td>${user.dept}</td>
            <td>${user.phone}</td>
            <td>${user.createTime}</td>
        </tr>
    </c:forEach>
</table>
</body>
</html>

输出:

image.png

注意:当我们配置项目到tomcat上时,在创建artifact时可以指定项目名称(不一定要是项目名)作为请求的路径,像这样接在localhost后面,http://localhost:8080/noodle/user/list。如果只是设置/ (斜杠)则直接在端口后面接请求地址。

疑问

Q:在springmvc容器的配置文件hellospringmvc-servlet.xml中配置开启mvc注解驱动的作用是什么?都作用在哪些地方?什么时候用到?

教程里的解释是在Spring中一般采用@RequestMapping注解来完成映射关系,要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例,这两个实例分别在类级别和方法级别处理。而<mvc:annotation-driven/>配置帮助我们自动完成上述两个实例的注入。 但是在实践中我们就算没有显式注册这两个bean实例或者在spring mvc配置的hellospringmvc-servlet.xml中加上 <mvc:annotation-driven/>这句配置也不妨碍我们使用@RequestMapping注解来完成映射关系,为什么?

  <!-- 3.开启mvc注解驱动-->
    <mvc:annotation-driven/>

Q:什么叫Ant风格的路径匹配功能?

A: ANT通配符有以下三种:

通配符 说明
? 匹配任何单字符
* 匹配0或者任意数量的字符
** 匹配0或者更多的目录

例子:

URL路径 说明
/app/*.x 匹配(Matches)所有在app路径下的.x文件
/app/p?ttern 匹配(Matches) /app/pattern 和 /app/pXttern,但是不包括/app/pttern
/**/example 匹配(Matches) /app/example, /app/foo/example, 和 /example
/app/*/dir/file. 匹配(Matches) /app/dir/file.jsp, /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java
/*/.jsp 匹配(Matches)任何的.jsp 文件

属性: 最长匹配原则(has more characters) 说明,URL请求/app/dir/file.jsp,现在存在两个路径匹配模式//.jsp和/app/dir/.jsp,那么会根据模式/app/dir/*.jsp来匹配

参考

Q:RequestMappingHandlerMapping会被默认创建么?在什么情况下创建,标记有@RequestMapping("/hello") 注解时还是扫描Controller时?

A:是在web.xml配置<mvc:annotation-driven/>时。RequestMappingHandlerMapping和RequestMappingHandlerAdapter会默认注册。

当然,一般情况下我们是不需要配置<mvc:annotation-driven/>的,默认会注册,但当我们web.xml中配置了其他如BeanNameUrlHandlerMapping处理映射器时,就要加这句,否则不会默认帮我们注册,这个需要研究下代码是怎么个处理方式。

在不配置HandlerMapping 的情况下,容器默认会注册初始化BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping来处理映射。而HandlerAdapter会有三种,分别是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,RequestMappingHandlerAdapter。

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
   org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

注意:如果有在web.xml中配置指定的HandlerMapping 和 HandlerAdapter 的话,则只注册配置的处理器。

mvc:annotation-driven的作用

Spring 3.0.x中使用了mvc:annotation-driven后,默认会帮我们注册默认处理请求,参数和返回值的类,其中最主要的两个类:DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter ,分别为HandlerMapping的实现类和HandlerAdapter的实现类,从3.1.x版本开始对应实现类改为了RequestMappingHandlerMapping和RequestMappingHandlerAdapter。

mvc:annotation-driven的作用参考

Q:如果我们没有配置HandlerAdapter ,默认是创建什么类型的HandlerAdapter 来处理我们的Controller呢?

A:默认不配置SimpleControllerHandlerAdapter, 也能处理Controller类型为org.springframework.web.servlet.mvc.Controller的接口,SimpleControllerHandlerAdapter会默认注册.

Q:保存HandlerMethod类型的容器mappingLookup为什么要初始化为new LinkedHashMap<>();而不是HashMap<>()类型呢?出于什么考虑?LinkedHashMap和HashMap各种的使用场景和优势缺点是什么?

A:考虑动态添加的效率?

Q:转发和重定向的区别?

Q:在目前主流的三种 Web 服务交互方案中,REST 相比于SOAP(Simple Object Access protocol, 简单对象访问协议) 以及 XML-RPC 更加简单明了。了解下目前主流的Web 服务交互方案。

Q:什么叫 Payload 的编码?

Q:一般实践中的RESTful风格的请求开发是通过请求方式POST、GET等的不同来区分的么?

Q:ApplicationContext和WebApplicationContext有什么区别?用他们去getBean()是一样的?

A:Spirng容器只有一个,spring和springmvc的配置可以互相放在各自的配置xml中,最后都作用在spring容器中。待详细了解。

Q:为什么叫Spring MVC、MyBatis整合Spring?而不是反过来叫Spring MVC整合Spring呢?是随便叫还是有什么区别?

Q:ContextLoaderListener的作用?

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

Q:@Repository注解在mybatis中有什么用么?一般不都是通过扫包来获得dao对象么?

//不需要加注解
//@Repository
//@Mapper
public interface UserMapper {

    List<User> getALlUsers();
}

其他:

1、md删除线使用:
如果段落上的文字要添加删除线,只需要在文字的两端加上两个波浪线 ~~ 即可
~~shanchu.com~~

2、DispatcherServlet.properties ——DispatcherServlet初始化默认配置

org.springframework.web.servlet.DispatcherServlet#defaultStrategies  jdk1.8 284行
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

3、IDEA的tomcat日志输出乱码问题的解决。

这是因为下载的tomcat8中\apache-tomcat-8.5.55\conf\logging.properties 配置默认编码是UTF-8.而Windows系统的默认编码格式是GBK,因此在对输出的字符打印时因为解码不对而导致的乱码。只要对logging.properties中的编码格式UTF-8配置注释掉即可。

参考

4、JDK1.8对接口的默认实现。

public interface HandlerInterceptor {
   default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {

      return true;
   }
 }

参考

Spring MVC教程——一点——蓝本

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352