美文网首页
Spring MVC教程——检视阅读(三)

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

作者: 卡斯特梅的雨伞 | 来源:发表于2020-05-14 16:16 被阅读0次

    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教程——一点——蓝本

    相关文章

      网友评论

          本文标题:Spring MVC教程——检视阅读(三)

          本文链接:https://www.haomeiwen.com/subject/nsotohtx.html