尚硅谷SpringMVC基础 笔记

SpringMVC笔记

【1】MVC

M;模型层,包含数据校验
V:视图层,包含国际化、标签库
C:控制层,包含转发重定向、参数、拦截器、作用域等 

【2】Spring中的父子容器问题

【3】@RequestMapping

    /**
     * value && path - 都是定义映射路径。取值前面的`/`表示映射到项目根目录,可以省略不写,但是从规范上建议写上
     * name  - 控制单元的注释
     * method - 表示只有指定类型请求方式才能访问这个控制单元方法,其他的请求方式访问时,响应会出现405状态码
     * params - 参数限制。限制请求必须有什么名字的请求参数,或这个参数的值必要格式是什么样的。
     * 		语法:{"参数名", "参数名2", "参数名3=必须的参数值正则表达式或固定值"}
     * 		如果请求中没有包含指定类型参数,响应会出现400状态码。并且明确提示在实际的请求参数中没有明确设置name属性
     * headers - 本次请求种,必须有的请求头名字或值的正则。
     * 		 语法: {"头名称", "头名称2=正则或固定值"}
     * 		 如果请求头于配置要求不符合,则404。
     * consumes - 请求的content-type限制,如果请求头的content-type和配置不符合,则415错误。平时多不设置,由Spring MVC自动判断
     * produces - 作用是设置@ResponseBody注解的响应内容类型。且仅当请求头中Accept中包含的值才生效。
     * 
     * @return
     */
    @RequestMapping(path = {"/add", "/add2", "/add1"}, name = "userController.add", method = {RequestMethod.GET},
            params = {"name", "age=20"},/* headers = {"Accept", "Host=localhost"},*//*consumes = {"text/html"},*/
            produces = {"text/plain; charset=UTF-8"}
    )
    public ModelAndView add(){
        System.out.println("add方法运行");
        ModelAndView mv = new ModelAndView();
        // viewName是结果视图的地址
        mv.setViewName("forward:/index.jsp");
        // model是请求作用域数据
        mv.addObject("t", "测试请求作用域传递数据");
        return mv;
    }

【4】请求参数的处理

普通参数: 请求参数没有同名,使用字符串,数字等类型变量处理的参数。
  - 在控制方法参数表中,定义同名的参数,自动处理。类型不匹配,则400错误
  - 如果请求参数名字和方法参数名字不同,使用注解@RequestParam(value="请求参数名") 修饰方法参数
  - 注解@RequestParam中的属性value可以省略,默认和方法参数同名
数组参数: 请求参数同名多个,是一个数组。eg: localhost:8080/bjsxt/test1?name=zs&name=ls
  - 在控制方法参数表中,定义同名的数组类型参数,自动处理。或使用注解修饰List集合参数,自动处理。
对象参数: 使用自定义的实体类型,处理若干请求参数。
  - 在控制方法参数表中,定义任意变量名称的应用类型参数,springmvc自动的基于自定义类型中的property同名处理请求参数,并封装对象。
  - 注意,不能再使用@RequestParam注解修饰方法参数了,一定是请求参数名和property名同名处理。
  - 同名处理请求参数的时候,类型也要匹配。
  - 如果普通参数和对象参数的属性同名,springmvc秉承着雨露均沾的想法,给所有方法参数赋值
普通参数如果是日期格式如何处理? java.util.Date
  - 1. 默认转换: 看自己个人电脑右下角的时间格式。一般都是yyyy/M/d HH:mm:ss。只要传递的参数字符串格式符合,即可自动转换。
     格式可以是年月日,或年月日时分秒。 不能只有时分秒。
  - 2. 指定转换: 使用注解实现转换 @DateTimeFormat。指定具体的格式。注解提供的格式是什么,请求参数格式必须符合。否则400错误。
  - 即使自定义类型中包含日期类型的属性,转换方案等同普通参数。
如果自定义类型有关系,如何处理? 如:A中有B类型的引用,或A中有B泛型的集合
  - 如果User类型中有命名是idCard的IdCard类型参数,那么请求参数名称是 idCard.IdCard类型中的属性名
  - 如果User类型中有命名是addressList的Address泛型List参数。那么请求参数名称是 addressList[下标].Address类型中的属性名。eg: idCard.cardNo=123&addressList[0].addr=address1&addressList[1].addr=address2
    @RequestMapping("/param8")
    public String param8(@RequestParam(name="n", required = false, defaultValue = "默认值") String name,
                         @RequestParam(name = "a", required = false, defaultValue = "0") int a,
                         @DateTimeFormat(pattern = "yyyy-MM-dd") @RequestParam(defaultValue = "2022-01-01") Date d){
        System.out.println(name);
        System.out.println("a = " + a);
        System.out.println("d = " + d);
        return "/index.jsp";
    }

    @RequestMapping("/param7")
    public String param7(User user){
        System.out.println(user);
        return "/index.jsp";
    }

    @RequestMapping("/param6")
    public String param6(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date d){
        System.out.println(d);
        return "/index.jsp";
    }

    @RequestMapping("/param5")
    public String param5(Date d){
        System.out.println("时间是:" + d);
        return "/index.jsp";
    }

    @RequestMapping("/param4")
    public String param4(User user, String name){
        System.out.println("user = " + user);
        System.out.println("name = " + name);
        return "/index.jsp";
    }

    @RequestMapping("/param3")
    public String param3(User user){
        System.out.println(user);
        return "/index.jsp";
    }

    @RequestMapping("/param2")
    public String param2(String[] hobbies, @RequestParam List<String> books){
        System.out.println("hobbies = " + Arrays.toString(hobbies) + " ; books = " +books);
        return "/index.jsp";
    }

    @RequestMapping("/param1")
    public String param1(@RequestParam(value="n") String name, int age){
        System.out.println("name = " + name + " ; age = " + age);
        return "/index.jsp";
    }

    @RequestMapping(value = "/testProduces", produces = {"text/plain; charset=UTF-8"})
    @ResponseBody
    public String testProduces(){
        System.out.println("方法运行");
        return "这是测试的返回结果";
    }

【5】@RequestParam

注解 RequestParam - 是专门处理请求参数的注解。是用于修饰方法参数的。

使用注解声明的描述方法参数和请求参数的对应关系及其他要求
value & name - 互为别名。相当于一个属性。 代表请求的参数名字。只能描述普通参数。忽略方法参数名称,使用配置的请求参数名称处理
required - boolean类型的属性。代表这个请求参数是否是必要的。默认值是true。如果未传递此参数,400系列错误
defaultValue - 默认值。仅在未传递请求参数时生效。给方法参数提供默认值

注意:这个请求参数的值,不符合Java习惯,统称未传递请求参数。
	如: 数学类型的转换异常, 仅在 "" 或 null转换数学类型时生效。 其他具体值,默认值无效
	如: 字符串转换,仅在 "" 或 null 时默认值生效
	如: 日期转换, 仅在 "" 或 null 时默认值生效

【6】获取Servlet相关对象

    /**
     * 按照SpringMVC处理请求参数的方式,在方法参数表中定义即可。但是有特殊情况。
     * SpringMVC可以为控制单元(方法)传入Servlet中的对象,除ServletContext外。
     *
     * 因为ServletContext是全局可访问的资源对象,可以随意操作,则有安全隐患。如果必须使用,可以通过请求对象,手工获取 request.getServletContext()
     * 基于SpringMVC可以为控制单元注入Servlet对象的方案,可以手工从请求对象中获取请求参数
     * 
     * request.getParameter(),这种处理请求参数的方式,是紧耦合处理方案。耦合的是Servlet-API。  
     * 通过控制单元方法参数,处理请求参数的方式是松耦合(弱耦合)方案。弱耦合相对于Servlet-API。
     * 
     * @param request
     * @param response
     * @param session
     * @return 
     * 
     */
    @RequestMapping("/testServlet")
    public String testServlet(
            HttpServletRequest request,
            HttpServletResponse response,
            HttpSession session
    //            , ServletContext servletContext // 会报错
    ){
        System.out.println(request);
        System.out.println(request.getParameter("name"));
        System.out.println(response);
        System.out.println(session);
        System.out.println(session.getAttribute("name"));
        System.out.println(request.getServletContext());
        return "/index.jsp";
    }

【7】请求头获取

    /**
     * 如何获取请求头, servlet中 request.getHeader。 处理请求头数据的方式和处理请求参数的方式几乎一样。
     * 请求头的处理,必须给方法参数定义注解@RequestHeader。默认只处理请求参数,不处理请求头。
     * 如果方法参数名,和请求头名称一致。注解的属性value,可以省略。且方法参数名和请求头名称忽略大小写。
     *
     * @param Accept
     * @param connection
     * @param host
     * @param h
     * @return
     */
    @RequestMapping("/testHeader")
    public String testHeader(@RequestHeader String Accept,
                             @RequestHeader String connection,
                             @RequestHeader(value = "host") String host,
                             @RequestHeader("Accept-Encoding") String h) {
        System.out.println("accept = " + Accept);
        System.out.println("connection = " + connection);
        System.out.println("host = " + host);
        System.out.println("accept-encoding = " + h);
        return "/index.jsp";
    }

【8】 请求转发和响应重定向

在SpringMVC的控制单元中,方法定义返回类型可以有多种。理论上是返回任意类型都对。包括void。
忽略AJAX请求处理方式,请求转发和响应重定向对应的控制单元返回值类型包括:
    1. String - 返回的字符串,默认就是结果视图的地址。
       默认是请求转发。实际上,类似指定请求转发方案  forward:/path
       响应重定向: redirect:/path
    2. ModelAndView - 使用Model传递请求作用域数据,使用view传递结果视图地址。
       默认的结果视图地址就是请求转发,可以使用类似的功能语法 forward:/path 提供viewName
       可以使用redirect:/path 提供重定向viewName
       如果使用ModelAndView实现重定向,那么其中的model请求作用域数据,会自动转换成同名请求参数传递
    3. void - 使用ServletAPI实现请求转发和重定
  注意:如果结果路径是其他的控制器RequestMapping地址,编程方式和普通视图返回处理一致。
  注意:如果返回的路径使用forward:或redirect:开头,路径和前缀一定要紧挨在一起
  注意:所有的返回结果地址,建议都使用 '/' 开头,都是相对应服务器的根开始寻址。准确。
    @RequestMapping("/servletRedirect")
    public void servletRedirect(HttpServletResponse response) throws IOException, ServletException{
        System.out.println("servletRedirect运行");
        response.sendRedirect("/index.jsp");
    }

    @RequestMapping("/servletForward")
    public void servletForward(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        System.out.println("servletForward运行");
        request.getRequestDispatcher("/index.jsp").forward(request, response);
    }

    @RequestMapping("/redirectMV")
    public ModelAndView redirectMV(){
        System.out.println("redirectMV运行");
        ModelAndView mv = new ModelAndView();
        mv.addObject("t", "测试重定向");
        mv.setViewName("redirect:/index.jsp");
        return mv;
    }

    @RequestMapping("/forwardMV")
    public ModelAndView forwardMV(){
        System.out.println("forwardMV运行");
        ModelAndView mv = new ModelAndView();
        mv.addObject("t", "测试数据传递");
        mv.setViewName("forward:/index.jsp");
        // mv.setViewName("/index.jsp");
        return mv;
    }

    @RequestMapping("/testRedirect")
    public String testRedirect(){
        System.out.println("testRedirect方法运行");
        return "redirect:/index.jsp";
    }

    @RequestMapping("/testForward")
    public String testForward(){
        System.out.println("testForward方法运行");
        return "forward:/index.jsp";
        // return "/index.jsp";
    }

【9】控制器中的方法返回值

1. void - 没有返回值。在只有@RequestMapping注解修饰方法的时候,代表返回的结果地址就是当前请求地址。
   相对路径是,最近一次请求的上级地址。
   如: 请求地址是 localhost:8080/add, 返回结果是 /add
   如: 请求地址是 localhost:8080/bjsxt/add, 返回结果是 /bjsxt/bjsxt/add
   当类型上没有RequestMapping注解定义统一的地址前缀时,方法返回值类型是void,会产生无限递归。
2. String - 只有RequestMapping注解修饰方法,代表返回的字符串,就是要显示的视图地址。默认请求转发。
   如果返回地址是  path, 相对于,最近请求的上级地址。 不推荐的写法。
   如果返回地址是  /path, 相对于应用的根。 建议写法
3. ModelAndView - SpringMVC通用返回结果类型。使用viewName属性确定视图结果地址。
   model决定请求作用域数据。

【10】视图解析器

用于视图跳转
如果JSP视图隐藏,一般都会保存到同一个隐藏保护目录中
如: /WEB-INF/jsp/ 中保存所有的JSP文件
     /WEB-INF/jsp/users/ 中保存所有和用户相关的JSP
     /WEB-INF/jsp/admin/ 中保存素有和管理员相关的JSP等。
所有的视图都是JSP文件
所有的视图地址有统一的前缀: /WEB-INF/jsp/
有统一的后缀: .jsp
如果可以提供前后缀拼接处理,则方法只需要返回中间变化的部分即可。
如果请求转发的视图,需要忽略视图解析器中的统一前后缀,可以增加返回字符串viewName的前缀 forward:
  • 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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd"
       default-autowire="default">
    <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>

    <!-- 让springmvc生效。
         设置默认的视图解析器是: InternalResourceViewResolver
     -->
    <mvc:annotation-driven></mvc:annotation-driven>

    <!-- 配置视图解析器,这是一个bean对象。这个对象,由springmvc框架自动使用。是基于byType找对象的。
         可以不配置唯一标记。 id或name
         这就是默认的视图解析器。
     -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 可以配置属性 -->
        <!-- 默认情况下,返回的字符串viewName,会自动拼接前后缀,组成完整的视图地址。 -->
        <!-- 统一的前缀 -->
        <!-- <property name="prefix" value="/WEB-INF/jsp/"></property> -->
        <!-- 统一的后缀 -->
        <!-- <property name="suffix" value=".jsp"></property> -->
    </bean>

</beans>
  • controller
    /** 
	* 当没有配置视图解析器前缀后缀时候, 默认请求转发。toSuibian2() 和 toSuibian3() 方法都可以跳转到 /WEB-INF/jsp/suibian.jsp
	* 如果没有配置视图解析器,Spring MVC 会做以下尝试
	* 	尝试使用默认的 InternalResourceViewResolver
	* 		如果项目包含了 WEB-INF/views/index.jsp 并且你的项目环境支持 JSP,这种情况下返回值可能会被解析为 /WEB-INF/views/index.jsp 并成功渲染
	*		如果没有找到匹配的 JSP 文件,Spring MVC 会抛出 404 错误
	*	直接作为路径访问
	*		如果没有视图解析器,并且返回值不符合 JSP 文件路径,它可能被解释为直接的请求路径。
	* 当配置了视图解析器后,会自动拼接前后缀。需要忽略视图解析器中的统一前后缀,可以增加返回字符串viewName的前缀 forward: 
	*/
	@RequestMapping("/toSuibian")
    public String toSuibian(){
        System.out.println("跳转到/WEB-INF/jsp/suibian.jsp");
        //return "suibian";
        return "/WEB-INF/jsp/suibian.jsp";
    }

    @RequestMapping("/toSuibian2")
    public String toSuibian2(){
        System.out.println("toSuibian2() 运行了");
        return "/toSuibian";
    }

    @RequestMapping("/toSuibian3")
    public String toSuibian3(){
        System.out.println("toSuibian3() 运行了");
        return "/WEB-INF/jsp/suibian.jsp";
    }

【11】变量作用域

请求变量作用域: request
 设置:
   1. 使用ModelAndView : 使用Model传递数据
   2. 使用控制单元(方法)参数,Map类型的参数。 直接在方法参数表中定义Map类型的参数,put的键值对,自动传递到请求作用域
   3. 使用控制单元参数, Model类型的参数。 在方法参数表中定义Model类型的参数,调用add系列方法,自动传递到请求作用域。
   4. 使用ServletAPI, request.setAttribute。 Servlet原生API。
 取值
   1. 使用ServletAPI, request.getAttribute
   2. 使用方法参数 + 注解 @RequestAttribute,让SpringMVC帮助从请求作用域中获取attribute。
   
会话变量作用域: session
  设置:
    1. 只能通过ServletAPI实现。
  取值:
    1. 使用ServletAPI获取, request.getSession().getAttribute() 或者 session.getAttribute()
    2. 使用方法参数 + 注解 @SessionAttribute 实现
    
应用上下文变量作用域: application。避免全局直接使用,存在的安全隐患。 不推荐使用。
  设置:
    1. 使用ServletAPI实现。 request.getServletContext().setAttribute()
  取值:
    1. 使用ServletAPI实现。 request.getServletContext().getAttribute()
    @RequestMapping("/getAppScope")
    public String getAppScope(HttpServletRequest request){
        System.out.println("application app = " + request.getServletContext().getAttribute("app"));
        return "/index.jsp";
    }

    @RequestMapping("/applicationScope")
    public String applicationScope(HttpServletRequest request){
        request.getServletContext().setAttribute("app", "通过application传递的数据");
        return "/index.jsp";
    }

    @RequestMapping("/getSessionScope")
    public String getSessionScope(@SessionAttribute("session1") String session1,
                                  @SessionAttribute String session2){
        System.out.println("session1 = " + session1);
        System.out.println("session2 = " + session2);
        return "/index.jsp";
    }

    @RequestMapping("/sessionScope")
    public String sessionScope(HttpServletRequest request, HttpSession session){
        request.getSession().setAttribute("session1", "基于request.getSession.setAttribute传递");
        session.setAttribute("session2", "基于session.setAttribute传递");

        return "/index.jsp";
    }

    @RequestMapping("/getReqScope")
    public String getReqScope(@RequestAttribute("msg") String msg,
                              @RequestAttribute String testMap){
        System.out.println("请求作用域: msg = " + msg);
        System.out.println("请求作用域: testMap = " + testMap);
        return "/index.jsp";
    }

    @RequestMapping("/reqScopeModel")
    public String reqScoopeModel(Model model){
        System.out.println(model.getClass().getName());
        // 相当于request.setAttribute
        model.addAttribute("msg", "使用Model传递的数据");
        Map<String, Object> attrs = new HashMap<>();
        attrs.put("testMap", "测试使用addAllAttributes传递数据");
        // 迭代map,使用map的key作为attribute名字,map的value作为attribute值,调用addAttribute方法。
        model.addAllAttributes(attrs);
        return "/bjsxt/getReqScope";
    }

【12】静态资源放行

  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 配置监听器,ContextLoaderListener,处理除MVC框架外的其他bean对象 -->
    <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>charset</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!-- 自定义具体的字符集, 默认字符集, ISO-8859-1 -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>charset</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 使用springmvc框架,必须手工配置servlet,也就是前端控制器 DispatcherServlet
         只管理MVC框架相关的bean对象
         1. 根据配置读取spring配置文件  spring-mvc.xml,创建一个WebApplicationContext类型的对象,并保存到application作用域中
            attribute名字是FrameServlet全命名.CONTEXT.servletName
         2. 使用WebApplicationContextUtils工具方法,从application作用域中找ContextLoaderListener创建的WebApplicationContext对象。
            2.1 如果找到了ContextLoaderListener创建的WebApplicationContext对象,则调用DispatcherServlet创建的applicationContext对象方法 setParent,记录父容器
            2.2 如果找不到ContextLoaderListener创建的WebApplicationContext对象,父容器就是null。

         子容器:也就是DispatcherServlet创建的WebApplicationContext可以获取父容器(Listener创建的WebApplicationContext)中的任意bean对象。
         父容器:不可以获取子容器中的bean对象。
         所以,子容器管理的控制器controller中,可以使用@Autowired自动注入父容器中管理的Service对象。
     -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 读取spring框架配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--
        / - 忽略常规意义上的视图地址。 如 .html
        /* - 全部
    -->
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
  • spring-mvc.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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd"
       default-autowire="default">
    <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>

    <!-- 让springmvc生效。
         设置默认的视图解析器是: InternalResourceViewResolver
     -->
    <mvc:annotation-driven></mvc:annotation-driven>

    <!-- 配置静态资源放行
         配置例外,当请求地址是什么的时候,前端控制器DispatcherServlet不处理。直接放行请求到静态资源。
         mapping - 请求地址的映射规则, 使用通配符配置。 如 /pic/*
         location - 静态资源在哪一个目录中,寻址方案是,把mapping的*统配部分作为目录中的资源查找。
         如: 请求地址是 /pic/1.webp, mapping配置的*对应 1.webp。在location文件夹/pic/中找 1.webp

         静态资源放行,相当于请求转发。实际上,这个标签,相当于配置了一个controller。
         mapping就是controller中方法的@RequestMapping地址。 location相当于方法的返回结果拼接方案。

         ant映射方式: 使用同配置配置映射地址。可用位置: 所有springmvc的映射地址配置。
         如: 静态资源放行、 @RequestMapping
           单级别统配: *。 如: /pic/* , 代表目录/pic/中的任意资源。不包括子目录。
                /pic/* 对应 /pic/1.webp  /pic/abc  /pic/123  /pic/000
           多级别统配: /**。 如: /pic/**, 代表目录/pic/中的任意资源,包括子目录。
               /pic/** 对应 /pic/1.webp  /pic/1/1.webp  /pic/abc  /pic/a/b/c
     -->
    <mvc:resources mapping="/pic/**" location="/pic/"></mvc:resources>
    <mvc:resources mapping="/pic2/**" location="/WEB-INF/pic/"></mvc:resources>

    <!-- 配置视图解析器,这是一个bean对象。这个对象,由springmvc框架自动使用。是基于byType找对象的。
         可以不配置唯一标记。 id或name
         这就是默认的视图解析器。
     -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 可以配置属性 -->
        <!-- 默认情况下,返回的字符串viewName,会自动拼接前后缀,组成完整的视图地址。 -->
        <!-- 统一的前缀 -->
<!--        <property name="prefix" value="/WEB-INF/jsp/"></property>-->
<!--        &lt;!&ndash; 统一的后缀 &ndash;&gt;-->
<!--        <property name="suffix" value=".jsp"></property>-->
    </bean>

</beans>
  • JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <img src="/pic2/2.webp">
    <img src="/pic/webp/1.webp">
    <h3>这是springmvc返回的视图</h3>
    <h3>请求作用域 msg : ${requestScope.msg}</h3>
    <h3>请求作用域 testMap : ${requestScope.testMap}</h3>
    <h3>会话作用域 session1 : ${sessionScope.session1}</h3>
    <h3>会话作用域 session2 : ${sessionScope.session2}</h3>
    <h3>上下文作用域 app : ${applicationScope.app}</h3>
    <img src="/pic/1.webp">
</body>
</html>

【13】Restful请求格式

Rest(Representational State Transfer:表现层状态转译)是一种软件架构风格,其核心是面向资源的一种设计。
Restful的出现同时也解决了客户端的种类多种多样造成请求的格式比较混乱的问题,Restful提供了一种统一的前后端交互的接口规范,可以更好的实现数据的交互。
通俗讲就是每个资源都有一个url地址,而不是不同的操作有不同的url地址。
package com.bjsxt.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * 视图跳转控制器 
 * 使用Restful可以只写一个控制器
 */
@Controller
public class PageDispatcherController {
    @GetMapping("/page/{model}/{view}")
    public String dispatch(@PathVariable String model, @PathVariable String view){
        String path = "/";
        if(!model.equals("root")){
            path += (model + "/");
        }
        path += (view + ".jsp");

        return path;
    }
}
package com.bjsxt.controller;

import com.bjsxt.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/**
 * 学习Restful相关知识
 *
 * DELETE 请求,没有请求体
 *  传参方式必须:
 *   1. ?传参
 *   2. restFul风格参数。  /path/参数
 *      springMVC的控制器,处理restful风格参数,必须给方法参数加注解。
 *      PathVariable(value="requestMapping中的{}内的变量名,如果和参数名一样可以省略属性")
 * PUT 请求, 没有请求体
 *  传参方式
 *   1. ?传参
 *   2. restFul风格参数。 /path/参数1/参数2/参数3/.../参数n
 *
 * restFul风格参数 不能处理 '/'。
 */
@Controller
public class TestRestController {
    /**
     * 现在有需要管理的资源 User。
     * 访问路径地址是 /user
     * 对此资源做管理操作 (CRUD)
     */

    /**
     * 路径地址 /user   请求方式 Post
     * @return
     */
    //@RequestMapping(value = "/user", method = {RequestMethod.POST})
    @PostMapping("/user")
    public String addUser(User user){
        System.out.println("新增 : " + user);
        return "/index.jsp";
    }


    @DeleteMapping("/user/{suibian}")
    @ResponseBody
    public String removeUser(@PathVariable("suibian") Integer id){
        System.out.println("删除: " + id);
        return "删除成功";
    }

    @PutMapping("/user/{id}/{name}")
    @ResponseBody
    public String modifyUser(@PathVariable Integer id, @PathVariable String name){
        System.out.println("修改:id = " + id + " , name = " + name);
        return "修改";
    }

    @GetMapping("/user/{id}")
    public String getUser(@PathVariable Integer id){
        System.out.println("查询 : " + id);
        return "/index.jsp";
    }
}

【14】@ResponseBody注解

package com.bjsxt.controller;

import com.bjsxt.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;

/**
 * 
 *  @ResponseBody注解控制器方法的返回值,通过响应输出流,输出到客户端浏览器。
 *  在向客户端输出数据的时候,是需要转换方法返回值 到 客户端(浏览器)可识别的内容的。
 * 
 *  根据控制单元(方法)返回值类型,决定输出到客户端时,ContentType响应头类型。
 *  返回String, content-type = text/html;charset=ISO-8859-1
 *  返回其他, content-type=application/json
 *  当方法增加@ResponseBody注解的时候,@RequestMapping注解的属性produces可以用于设置响应头contentType
 * 
 *  这个转换是基于SpringMVC框架中的HttpMessageConverter Http协议消息转换器实现的。
 *  需要项目中导入依赖 jackson 相关依赖。 才能让HttpMessageConverter 生效。
 *  SpringMVC 认为,正常情况下, 响应输出流向客户端输出基本数据类型的可能性很低。没有提供对应的HttpMessageConverter实现
 * 
 *  使用的是通用实现。可以删除produces属性。做尝试。不同的springmvc版本jar包,提供不能的服务能力。
 *  所谓通用实现,根据导入的jackson相关依赖不同,使用不同的实现。
 *  
 *  jackson-databind - 通用实现是json转换。
 *  1. 基本数据类型 - 默认设置响应头 content-type = application/json。 把方法返回值直接输出到客户端浏览器。
 *  2. String类型 - 默认设置响应头 content-type=text/html;charset=ISO-8859-1。
 *     认为返回的字符串,是一个可显示的HTML视图。把字符串返回值直接输出到客户端。
 *     可以通过Mapping的属性produces设置响应头,规避乱码问题。
 *  3. 其他引用类型 - 设置响应头 content-type=application/json。
 *     使用HttpMessageConverter中的json转换器,把Java对象,转换成JSON格式字符串。
 *     转换过程,基于property。property的名字是json中的属性名,property的value是json中的属性值。
 *     注意:返回的引用类型中,getter和setter必须成对出现,除getClass外。
 *     异常情况: 当处理此数据的反序列化时,会抛出异常。 把JSON格式字符串,转换成Java类型对象时。
 *               且jackson-databind版本过低的时候,可能在java转换json格式字符串的时候,就会抛出异常。
 *  4. 集合类型 - 设置响应头 content-type=application/json。
 *     Map - 效果等用引用类型对象。
 *     List和Set - 是增加了JSON数组的引用类型对象。也就是[{},{}]
 * 
 *  jackson-dataformat-xml - 响应结果使用XML展示。
 *  响应结果使用XML展示。 需要修改依赖,使用jackson-dataformat-xml替换jackson-databind。
 *  1. 基本数据类型 - 默认 content-type = application/json
 *  2. 字符串 - 默认的content-type = text/html;charset=ISO-8859-1
 *  3. 引用类型和集合类型 - 默认 content-type=application/xhtml+xml
 *  5. 普通引用 - 返回的xml格式是 <类型名称><属性名称>属性值</属性名称><属性名称>属性值</属性名称></类型名称>
 *  List - <List><Item><属性名>属性值</属性名></Item></List>
 *  Set - <Set><Item><属性名>属性值</属性名></Item></Set>
 *  Map - <Map><key>value</key></Map>
 *
 *  可修饰的资源:
 *   1. 方法: 当前方法的返回值,基于响应输出流输出到客户端。
 *   2. 类型: 当前类型中所有方法的返回值,基于响应输出流输出到客户端。
 *   特殊: 修饰方法返回值,等同修饰方法。部分程序员的编写习惯。
 *
 * RestController注解, 相当于Controller+ResponseBody。
 *
 */
//@Controller
//@ResponseBody
@RestController
public class TestResponseBodyController {

    @GetMapping("/testSet")
//    @ResponseBody
    public Set<User> testSet(){
    //public @ResponseBody Set<User> testSet(){
        System.out.println("返回Set集合");
        Set<User> set = new HashSet<>();
        set.add(new User(1, "吕布"));
        set.add(new User(2, "貂蝉"));
        return set;
    }

    @GetMapping("/testList")
//    @ResponseBody
    public List<User> testList(){
        System.out.println("返回List集合");
        List<User> list = new ArrayList<>();
        list.add(new User(1, "关羽"));
        list.add(new User(2, "张飞"));
        list.add(new User(3, "刘备"));
        return list;
    }

    @GetMapping("/testMap")
//    @ResponseBody
    public Map<String, Object> testMap(){
        System.out.println("返回Map集合");
        Map<String, Object> map = new HashMap<>();
        map.put("name", "李四");
        map.put("gender", "男");

        return map;
    }

    @GetMapping("/testRef")
//    @ResponseBody
    public User testReturnReference(){
        System.out.println("返回引用类型对象");
        User user = new User();
        user.setId(10);
        user.setName("张三");
        return user;
    }

    @GetMapping(value = "/testStr", produces = {"text/html; charset=UTF-8"})
//    @ResponseBody
    public String testReturnString(){
        System.out.println("返回字符串");
        return "返回值";
    }

    @GetMapping(value = "/testInt", produces = {"application/json; charset=UTF-8"})
//    @ResponseBody
    public int testReturnInt(){
        System.out.println("测试返回整数");
        return 1;
    }

}
  • 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>sj125_springmvc</artifactId>
        <groupId>com.bjsxt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sj125_springmvc_2</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.9.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10.8</version>
        </dependency>
        <!--<dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.9.9</version>
        </dependency>-->
    </dependencies>
    <pluginRepositories>
        <pluginRepository>
            <id>mvnrepository</id>
            <url>https://artifacts.alfresco.com/nexus/content/repositories/public/</url>
        </pluginRepository>
    </pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat8-maven-plugin</artifactId>
                <version>3.0-r1756463</version>
                <configuration>
                    <port>80</port>
                    <path>/</path>
                </configuration>

            </plugin>
        </plugins>
    </build>
</project>

【15】@RequestBody注解

在客户端中无论使用的是`<form>`表单,还是Ajax请求,默认的请求时内容类型都是application/x-www-form-urlencoded,DataFormate 中 数据请求参数用&连接。eg: ie=utf-8&mod=1 
package com.bjsxt.controller;

import com.bjsxt.pojo.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
public class TestRequestBodyController {
    /**
     * 请求参数中,包含一个用户对象。
     *
     * 使用要求:
     *  1. 请求方式必须POST
     *  2. 请求content-type必须是application/json
     *  3. 请求参数使用请求体传递,必须是JSON格式字符串。
     *
     * RequestBody参数,使用JSON描述请求参数,理论上可以传递任何参数。
     * 如:自定义对象,List,数组,Map等。
     * 底层也是使用jackson-databind实现的。
     *
     * @param user
     * @return
     */
    @PostMapping("/testRequestBodyUser")
    public User testRequestBody1(@RequestBody User user){
        System.out.println(user);
        return user;
    }
    
    @PostMapping("/listUser")
    public List<User> listUser(@RequestBody List<User> userList){
        System.out.println(userList);
        return userList;
    }

    @PostMapping("/listMap")
    public List<Map<String, Object>> listMap(@RequestBody List<Map<String, Object>> listMap){
        System.out.println(listMap);
        return listMap;
    }

    @PostMapping("/map")
    public Map<String, Object> map(@RequestBody Map<String, Object> map){
        System.out.println(map);
        return map;
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="/js/jquery.js"></script>
    <script>
        /**
         * 当请求content-type=application/json时,
         * 使用ajax发起请求,一定要设置dataType(响应数据类型)。
         * 只有content-type和dataType同时设置,请求体参数,才会封装成json对象。
         * 否则都是 参数名=参数值&参数名=参数值 的字符串|表单域。
         *
         * 请求参数JSON对象,被AJAX处理后,转换成了名值对表单参数。
         * 使用JSON.stringify(JSON对象),手工转换对象成为JSON格式字符串。可以实现请求体传递JSON格式字符串。
         *
         * JSON.stringify(JSON对象) - 是把参数对象,转换成JSON格式字符串的方法。
         * JSON对象  {"id":1, "name":"admin"}
         * JSON格式字符串是   "{\"id\":1, \"name\":\"admin\"}"   '{"id":1, "name":"admin"}'
         *
         */
        $(function () {
            $("#btn").on('click', function () {
                var data = {"id":$("#id").val(), "name":$("#name").val()};
                $.ajax({
                    'url' : '/testRequestBodyUser',
                    'type' : 'post',
                    'contentType' : "application/json",
                    'data' : JSON.stringify(data),
                    //'data' : '{"id":3, "name":"abc"}',
                    'success' : function (data) {
                        alert(data);
                    },
                    'dataType':'json' // 响应的
                });
            })

            $("#mapBtn").on('click', function () {
                var data = {"name":"张三", "gender" : "男"};
                $.ajax({
                    'url': '/map',
                    'type': 'post',
                    'data':JSON.stringify(data),
                    'contentType': 'application/json',
                    'dataType': 'json',
                    'success': function (data) {
                        alert(data);
                    }
                });
            })

            $("#listMapBtn").on('click', function () {
                var data = [];
                data.push({"name":"李四", "gender":"男"});
                data.push({"name":"王五", "gender":"男"});
                $.ajax({
                    'url': '/listMap',
                    'type': 'post',
                    'data':JSON.stringify(data),
                    'contentType': 'application/json',
                    'dataType': 'json',
                    'success': function (data) {
                        alert(data);
                    }
                });
            })

            $("#listUserBtn").on('click', function () {
                var data = [];
                data.push({"id":1, "name":"赵四"});
                data.push({"id":2, "name":"刘能"});
                $.ajax({
                    'url': '/listUser',
                    'type': 'post',
                    'data':JSON.stringify(data),
                    'contentType': 'application/json',
                    'dataType': 'json',
                    'success': function (data) {
                        alert(data);
                    }
                });
            })
        });
    </script>

</head>
<body>
    <div style="width: 800px; margin:auto">
        <form method="post" action="/testRequestBodyUser">
            主键:<input type="text" name="id" id="id"><br>
            姓名:<input type="text" name="name" id="name"><br>
            <input type="button" value="提交" id="btn">
        </form>
        <input type="button" value="测试Map" id="mapBtn"><br>
        <input type="button" value="测试List<Map>" id="listMapBtn"><br>
        <input type="button" value="测试List<User>" id="listUserBtn">
    </div>
</body>
</html>

【16】SpringMVC中文乱码问题

Spring MVC默认没有去解决中文乱码问题。需要根据请求的方式的类型去选择对应的中文乱码解决方案。常见的两种请求方式就是GET和POST

GET方式中文乱码解决
GET方式中文乱码需要开发者自己进行转码。因为Tomcat默认的接收请求GET方式的是IOS-8859-1,所以无论客户端传递过来的数据是哪种编码方式,都会被Tomcat转换为ISO-8859-1.如果希望正确的显示中文,还需要把内容转换为UTF-8编码。

POST方式中文乱码解决
在Spring MVC提供了一个类CharacterEncodingFilter,里面直接写好了POST方式中文乱码解决代码
    // GET 方式中文乱码解决
	@RequestMapping("/testEncoding")
    public String testEncoding(String name) throws UnsupportedEncodingException {
        System.out.println("接收到name:"+name);
        String newName = new String(name.getBytes("iso-8859-1"),"utf-8");
        System.out.println("转换后的name:"+newName);
        return "suiyi";
    }
<!-- web.xml 解决 Spring MVC POST方式中文乱码 -->

<!--配置字符编码过滤器-->
<filter>
    <filter-name>code</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>code</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

【17】文件上传

Spring MVC 文件上传有如下几点要求:
1)客户端:
	请求方式必须是POST
	enctype必须为multipart/form-data
2)服务端:
 	必须配置MultipartResovler。否则无法解析上传文件的流数据。(<bean>的id值必须叫做multipartResovler)如果没有配置MultipartResovler不仅仅是文件流数据无法解析,连带着其他表单域数据也无法解析。因为文件流数据和表单数据都在请求体中,不解析的话,文件流数据和表单数据都接收不到
	注意文件域的name取值,文件域必须MultipartFile类型接收。且name的取值必须和MultipartFile对象名相同
<?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>sj125_springmvc</artifactId>
        <groupId>com.bjsxt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sj125_springmvc_2</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.9.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10.8</version>
        </dependency>
        <!-- 文件上传 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
    </dependencies>
    <pluginRepositories>
        <pluginRepository>
            <id>mvnrepository</id>
            <url>https://artifacts.alfresco.com/nexus/content/repositories/public/</url>
        </pluginRepository>
    </pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat8-maven-plugin</artifactId>
                <version>3.0-r1756463</version>
                <configuration>
                    <port>80</port>
                    <path>/</path>
                </configuration>

            </plugin>
        </plugins>
    </build>
</project>
<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd"
       default-autowire="default">

    <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>

    <mvc:annotation-driven></mvc:annotation-driven>

    <!-- 静态资源放行 -->
    <mvc:resources mapping="/js/**" location="/js/"></mvc:resources>

    <!-- 文件上传解析器 id要求是multipartResolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 限制上传文件大小, 常用 -->
        <property name="maxUploadSize" value="102400000000000"></property>
        <!-- 设置上传的文本文件默认字符集 -->
        <property name="defaultEncoding" value="UTF-8"></property>
        <!-- 设置内存缓冲区大小 -->
        <property name="maxInMemorySize" value="102400"></property>
    </bean>

</beans>
package com.bjsxt.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.UUID;

/**
 * 上传文件
 * 需要依赖  commons-io  和  commons-fileupload。 都是apache提供的。
 * maven资源管理中, commons-fileupload 依赖 commons-io。
 * <p>
 * 上传文件的客户端要求:
 * 1. 必须是POST请求
 * 2. content-type必须是multipart/form-data
 * 3. 上传文件的表单域标签是<input type="file">
 * <p>
 * 上传文件的SpringMVC环境要求
 * 1. 必须提供一个MultipartResolver解析器bean对象。
 * <p>
 * 控制单元要求
 * 1. 方法参数类型使用 MultipartFile 类型参数,处理上传的文件
 * 2. 如果方法参数名和请求参数中的上传文件参数名相同,可以省略注解@RequestParam。如果不同,则必须定义注解@RequestParam,并设置value属性。
 * value属性是请求参数名。
 * <p>
 * 类型MultipartFile的常用方法
 * 1. getName - 返回上传文件的请求参数名
 * 2. getOriginalFileName - 返回上传的文件原始文件名
 * 3. getInputStream - 返回上传的文件的输入流
 * 4. transferTo - 提供文件对象,可以直接把上传的文件保存到磁盘。
 * 复制上传文件到指定位置。方法参数就是要保存的文件对象。
 * <p>
 * 如果需要做上传限制:
 * 1. 限制上传文件的大小, 可以使用MultipartResolver限制。通过property配置限制。
 * 2. 限制上传文件的类型, MultipartResolver未提供上传文件类型限制。只能
 * 代码手工限制。如:文件后缀名, 请求文件类型等。
 */
@Controller
public class UploadFileController {
    @RequestMapping("/upload")
    @ResponseBody
    public String upload(@RequestParam("uploadFile") MultipartFile file, HttpServletRequest request) throws Exception {
        System.out.println("getName : " + file.getName());
        System.out.println("getOriginalFileName : " + file.getOriginalFilename());
        System.out.println("getInputStream : " + file.getInputStream());

        // file.transferTo(new File("D:\\images\\"+file.getOriginalFilename()));

        // 文件类型
        String type = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        // 要求只能上传图片,类型包括 jpg jpeg png gif webp 等
        if (!".png".equalsIgnoreCase(type)) {
            // 文件类型错误
            return "error file type";
        }

        // 动态生成文件名字
        // 使用UUID生成,或者使用时间戳生成。
        String originalFileName = file.getOriginalFilename();
        // 获取文件后缀
        String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
        //        // 生成唯一UUID
        String fileName = UUID.randomUUID().toString() + suffix;

        /*
         * 个别系统下,可能会出现bug。可能是windows问题或tomcat8的问题。
         * 有可能D:\\有 images(和getRealPath参数一样) 文件夹。 getRealPath的返回值是 D:\\images
         * 解决办法,删除D盘和其他盘符下的 同名 文件夹即可。
         */
        String dir = request.getServletContext().getRealPath("/images/");
        System.out.println(dir);
        File dirFile = new File(dir);
        if (!dirFile.exists()) {
            dirFile.mkdir();
        }

        // 把上传的文件,写到D:/images目录中。
        // 定义文件输出流
        FileOutputStream outputStream = new FileOutputStream(dir + "\\" + fileName);
        // 获取文件输入流
        InputStream inputStream = file.getInputStream();
        // 定义字节数组
        byte[] tmp = new byte[1024];
        // 边读边写
        int length = 0;
        while ((length = inputStream.read(tmp)) != -1) {
            outputStream.write(tmp, 0, length);
        }

        // 关闭流,只关闭自己创建的流。
        outputStream.close();

        return "upload finish";
    }
}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <div style="width: 800px; margin:auto">
        <form action="upload" method="post" enctype="multipart/form-data">
            <input type="file" name="uploadFile">
            <br>
            <input type="submit" value="上传">
        </form>
    </div>
</body>
</html>

【18】文件上传和下载案例

功能如下: 
1. 实现文件上传	
2. 上传成功后的结果,响应重定向到控制器path地址。 地址是 /showAllPic
3. 新增showAllPic功能。访问控制器,调用服务,调用mapper,查询数据库中所有已上传的文件数据,并在
    show.jsp页面中显示全部信息。
4. 修改show.jsp中的显示效果。把图片的地址修改成在线预览图片内容。也就是<img src="pic.path">标签
    增加控制方法,路径地址是 /pic, 参数名是path,值是图片地址。由服务器返回图片内容。
5. 修改show.jsp中的显示效果。给图片名称增加超链接,使用新页面的形式打开超链接。实现下载图片。
    要求:下载的文件的名字是上传的文件原始名字。
    下载地址是  /download, 请求参数是 id,值是图片的主键。
    从数据库查询图片完整信息,把服务磁盘中的文件读取并写到客户端,实现下载。数据库中的原始名称
    是下载的文件名字
    
文件下载:
 服务器返回的响应头 content-disposition 默认值是inline,含义是:浏览器可以识别的资源直接显示。不能识别的资源,直接下载
<?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>sj125_springmvc</artifactId>
        <groupId>com.bjsxt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sj125_springmvc_3_homework</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.9.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10.8</version>
        </dependency>
        <!-- 文件上传 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.7</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!--<dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.9.9</version>
        </dependency>-->
    </dependencies>
    <pluginRepositories>
        <pluginRepository>
            <id>mvnrepository</id>
            <url>https://artifacts.alfresco.com/nexus/content/repositories/public/</url>
        </pluginRepository>
    </pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat8-maven-plugin</artifactId>
                <version>3.0-r1756463</version>
                <configuration>
                    <port>80</port>
                    <path>/</path>
                </configuration>

            </plugin>
        </plugins>
    </build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext-*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <filter>
        <filter-name>charset</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>charset</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc/applicationContext-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
</web-app>
<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd"
       default-autowire="default">
    <context:component-scan base-package="com.bjsxt.service"></context:component-scan>
</beans>
<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd"
       default-autowire="default">

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mvc?serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"></property>
        <property name="password" value="root"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.bjsxt.pojo"/>
        <property name="mapperLocations" value="classpath:mappers/PicMapper.xml"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
        <property name="basePackage" value="com.bjsxt.mapper"/>
    </bean>

    <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="*" rollback-for="java.lang.Exception"/>
            <tx:method name="get*" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="txPc" expression="execution( * com.bjsxt.service.*.* (..) )"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPc"/>
    </aop:config>
</beans>
<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd"
       default-autowire="default">

    <context:component-scan base-package="com.bjsxt.controll"></context:component-scan>

    <mvc:annotation-driven></mvc:annotation-driven>

    <mvc:resources mapping="/dir/**" location="/dir/"></mvc:resources>

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

</beans>
package com.bjsxt.controll;

import com.bjsxt.pojo.Pic;
import com.bjsxt.service.PicService;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

/**
 * 控制器
 */
@Controller
public class PicController {
    @Autowired
    private PicService picService;

    /**
     * 下载,需要通过响应输出流向客户端输出。
     * @param id
     */
    @GetMapping("/download")
    public void download(Integer id, HttpServletResponse response){
        try {
            // 查询图片
            Pic pic = picService.getById(id);
            // 创建文件输入流
            FileInputStream inputStream = new FileInputStream(pic.getPath());
            // 获取响应输出流
            OutputStream outputStream = response.getOutputStream();
            // 设置响应头,当前响应是一个输出流,要做文件下载。 代表是已流的形式向客户端输出任意内容。
            response.setContentType("application/octet-stream");
            // 设置响应头 content-disposition,设置下载附件及附件名称
            // attachment 下载附件, filename, 附件的名字。
            response.setHeader("content-disposition", "attachment;filename="+pic.getOriginalName());

            // 边读边写
            IOUtils.copy(inputStream, outputStream);
            outputStream.flush();
            inputStream.close();

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 在线预览图片方法
     */
    @GetMapping("/pic")
    public void showOnline(String path, HttpServletResponse response){
        try {
            // 先找到服务器中的图片
            // 创建文件输入流
            FileInputStream inputStream = new FileInputStream(path);

            // 使用响应输出流向客户端输出图片内容
            // 获取响应输出流
            ServletOutputStream outputStream = response.getOutputStream();

            // 使用commons-io中的工具,调用方法,实现边读边写。
            IOUtils.copy(inputStream, outputStream);

            outputStream.flush();

            inputStream.close();
        }catch (IOException e){
            e.printStackTrace();
            // 图片找不到
            // 响应输出流,通知客户没有图片
            try {
                response.getOutputStream().flush();
            }catch (Exception e1){
                e1.printStackTrace();
            }
        }
    }

    /**
     * 显示所有的图片
     * @return
     */
    @GetMapping("/showAllPic")
    public String showPic(HttpSession session){
        // 查询图片
        List<Pic> list = picService.getAll();
        // 作用域传值
        session.setAttribute("list", list);
        // 重定向到show.jsp
        return "redirect:/show.jsp";
    }

    /**
     * 上传文件
     *
     */
    @PostMapping(value = "/uploadFile", produces = {"text/html; charset=UTF-8"})
    // @ResponseBody
    public String uploadFile(MultipartFile uploadFile, HttpServletRequest request){
        try {
            int flag = picService.uploadPic(uploadFile, request);
            if(flag == -1){
                // 保存文件到磁盘错误
                return "redirect:/uploadFail.jsp";
            }
            return "redirect:/showAllPic";
        }catch (RuntimeException e){
            e.printStackTrace();
            // 保存数据到数据库错误
            return "redirect:/uploadFail.jsp";
        }
    }
}
package com.bjsxt.service;

import com.bjsxt.pojo.Pic;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * 服务接口
 */
public interface PicService {
    /**
     * 主键查询图片
     * @param id
     * @return
     */
    Pic getById(Integer id);

    /**
     * 查询所有图片
     * @return
     */
    List<Pic> getAll();

    /**
     * 上传文件服务方法。
     * @param uploadFile
     * @return
     */
    int uploadPic(MultipartFile uploadFile, HttpServletRequest request);
}

package com.bjsxt.service.impl;

import com.bjsxt.mapper.PicMapper;
import com.bjsxt.pojo.Pic;
import com.bjsxt.service.PicService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

/**
 * 服务实现类
 */
@Service
public class PicServiceImpl implements PicService {
    @Autowired
    private PicMapper picMapper;

    @Override
    public Pic getById(Integer id) {
        return picMapper.selectById(id);
    }

    /**
     * 查询所有图片
     * @return
     */
    @Override
    public List<Pic> getAll() {
        return picMapper.selectAll();
    }

    /**
     * 上传文件
     *  1. 保存到服务器目录中
     *  2. 封装实体Pic对象
     *  3. 保存到数据库
     * @param uploadFile
     * @return
     *   -1 保存文件错误
     *   1  上传成功
     *   抛出运行时异常  保存数据到数据库错误
     */
    @Override
    public int uploadPic(MultipartFile uploadFile, HttpServletRequest request) {
        String dirPath = null;
        String fileName = null;
        try {
            // 保存到服务器目录中。
            // 获取服务器目录
            dirPath = request.getServletContext().getRealPath("/images/");
            // 判断目录是否存在,如果不存在,则创建目录
            File dir = new File(dirPath);
            if (!dir.exists()) {
                // 目录不存在
                dir.mkdir();
            }
            // 生成动态文件名称;
            fileName = UUID.randomUUID().toString();
            // 获取文件原始名称
            String originalFileName = uploadFile.getOriginalFilename();
            // 截取文件后缀
            String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
            fileName += suffix;

            // 保存
            uploadFile.transferTo(new File(dir, fileName));

            // 封装实体对象
            Pic pic = new Pic();
            pic.setOriginalName(originalFileName);
            // File.separator - 自动识别操作系统,返回目录分隔符。 windows \   Linux /
            pic.setPath(dirPath + File.separator + fileName);

            // 保存到数据库
            picMapper.insertPic(pic); // 有抛出异常的可能。 是运行时异常。
        }catch (IOException e){
            e.printStackTrace();
            // 保存文件到磁盘错误
            return -1;
        }catch (RuntimeException e){
            e.printStackTrace();
            // 把保存在磁盘上的文件删除。
            File file = new File(dirPath+"/"+fileName);
            file.delete();
            // 保存数据到数据库错误,抛出异常,让spring的声明式事务回滚。

            throw e;
        }

        return 1;
    }
}
package com.bjsxt.mapper;

import com.bjsxt.pojo.Pic;

import java.util.List;

/**
 * 数据访问接口
 */
public interface PicMapper {
    /**
     * 主键查询
     * @param id
     * @return
     */
    Pic selectById(Integer id);

    /**
     * 新增
     * @param pic
     * @return
     */
    int insertPic(Pic pic);

    /**
     * 查询所有的图片
     * @return
     */
    List<Pic> selectAll();
}
package com.bjsxt.pojo;

import java.io.Serializable;
import java.util.Objects;

/**
 * 实体
 */
public class Pic implements Serializable {
    private Integer id;
    private String originalName;
    private String path;

    public Pic() {
    }

    @Override
    public String toString() {
        return "Pic{" +
                "id=" + id +
                ", originalName='" + originalName + '\'' +
                ", path='" + path + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Pic pic = (Pic) o;
        return Objects.equals(id, pic.id) &&
                Objects.equals(originalName, pic.originalName) &&
                Objects.equals(path, pic.path);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, originalName, path);
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getOriginalName() {
        return originalName;
    }

    public void setOriginalName(String originalName) {
        this.originalName = originalName;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
}

【19】getRealPath()


【20】跨域@CrossOrigin

当前项目的协议、ip、端口和访问的URL的协议、IP、端口中有一个不同,这种访问就叫跨域
只要在控制单元方法上添加了@CrossOrigin注解后,会在响应头中添加Access-Control-Allow-Origin:*
Access-Control-Allow-Origin是HTTP协议中允许哪些IP的项目跨域访问,*表示所有IP
     /**
     * CrossOrigin注解 - 当前方法支持跨域访问。增加响应头。
     * 这个注解可以修饰方法,代表当前方法支持跨域访问。
     * 可以修饰类型,代表当前类型中的所有方法支持跨域访问
     *
     * 一般跨域处理都在同甲方应用中出现。
     * 如:腾讯的QQ主站、QQ音乐、QQ游戏等站点间,支持跨域。
     *
     * @return
     */
    @GetMapping(value = "/test", produces = {"text/html; charset=UTF-8"})
    @ResponseBody
    @CrossOrigin
    public String test(HttpServletResponse response, String flag) throws IOException {
        //response.setHeader("Access-Control-Allow-Origin", "*");
        System.out.println("test方法运行");
        if(flag == null){
            throw new IOException("测试其他异常");
        }
        return "test方法返回结果";
    }
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="/js/jquery.js"></script>
    <script>
        $(function () {
            $("#btn81").click(function () {
                // 同源访问,不是跨域
                $.ajax({
                    'url': '/test',
                    'type': 'get',
                    'success': function (data) {
                        alert(data);
                    }
                });
            })

            $("#btn80").click(function () {
                // 跨域访问
                $.ajax({
                    'url': 'http://localhost:80/test',
                    'type': 'get',
                    'success': function (data) {
                        alert(data);
                    }
                });
            })
        });
    </script>
</head>
<body>
<h3>测试页面</h3>
<input type="button" value="访问81端口test" id="btn81">
<br>
<input type="button" value="访问80端口test" id="btn80">
</body>
</html>

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd"
       default-autowire="default">

    <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>

    <mvc:annotation-driven></mvc:annotation-driven>

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

    <mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
</beans>

【21】拦截器

Servlet 中过滤器的作用是保护请求的服务器资源,在请求资源被执行之前,如果请求地址符合拦截范围,则会先执行过滤器
SpringMVC 中Servlet只有一个是DisptcherServlet,如果仍然使用过滤器来完成请求的拦截,就会造成拦截DispatcherServlet所有的请求。那么,如果部分请求不想被拦截,SpringMVC的拦截器来实现单元方法的拦截
拦截器的执行是在DispatcherServlet之后和单元方法之前的,这样我们就可以在单元方法被之前之前对请求进行自定义的拦截处理了
注意:只有URL匹配到了控制单元,拦截器才能生效
package com.bjsxt.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 拦截器 必须实现接口, 接口中所有的方法,都由default实现
 * HandlerInterceptor
 * 拦截器必须配置才能生效。可以采用xml配置,或Configuration配置
 */
public class FirstInterceptor implements HandlerInterceptor {
    /**
     * 控制方法运行前 拦截。
     *
     * @param request  请求对象
     * @param response 响应对象
     * @param handler  控制器对象
     * @return true - 继续运行后续的拦截器或控制方法。
     * false - 直接结束,不运行后续的拦截器或控制方法。立刻返回200状态码,响应无任何数据。
     * 一般return false,都会配合响应的sendError或抛出异常实现。
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("第一个拦截器, preHandle方法, handler = " + handler + " , handler类型 : " + handler.getClass().getName());
        /*if(request.getParameter("flag") != null){
            System.out.println("错误的请求参数");
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return false;
        }*/
        return true;
    }

    /**
     * 控制方法运行后 拦截,控制方法抛出异常,不运行。 不要轻易的改变ModelAndView对象的值。
     *
     * @param request      请求对象
     * @param response     响应对象
     * @param handler      控制器对象
     * @param modelAndView 控制方法返回值
     *                     控制方法返回值是ModelAndView,此参数就是方法返回值。
     *                     如果控制方法返回值是String,且没有ResponseBody,此参数中的viewName是方法返回值
     *                     如果控制方法参数表中有Model,此参数中的Model部分就是参数表的Model。
     *                     如果控制方法没有返回值void。此参数的viewName就是请求路径。
     *                     如果方法使用ResponseBody注解修饰,此参数不含Model和viewName。
     * @throws Exception 方法可以抛出的异常
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("第一个拦截器,postHandle方法, handler = " + handler + " , handler 类型 : " +
                handler.getClass().getName() + " , modelAndView = " + modelAndView);
    }

    /**
     * 控制方法运行后 拦截,控制方法是否抛出异常,都运行
     *
     * @param request  请求
     * @param response 响应
     * @param handler  控制器
     * @param ex       异常对象
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("第一个拦截器,afterCompletion方法, handler = " + handler + " , handler类型 : " + handler.getClass().getName()
                + " , ex = " + ex);
    }
}
<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd"
       default-autowire="default">

    <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
    
    <mvc:annotation-driven ></mvc:annotation-driven>

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

    <!--配置拦截器
        拦截器运行顺序,和全局于局部拦截器无关。只看配置顺序。由上至下,依次运行。
        先运行pre的,后结束afterCompletion
        底层是方法调用链。
        所有的拦截器保存在List中。控制方法运行前,正向迭代集合,调用拦截器preHandle方法。
        控制方法执行后,逆向迭代集合,调用拦截器postHandler方法。
        控制方法完全结束后(视图解析后或抛出异常),逆向迭代集合,调用拦截器afterCompletion方法。
        源码在DispatcherServlet类型中的doService和doDispatch方法中。
    -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.bjsxt.interceptor.ThirdInterceptor"></bean>
        </mvc:interceptor>
        <!--直接配置bean对象,代表全局拦截器,拦截所有的控制器方法。这个bean对象必须是HandlerInterceptor兼容类型。-->
        <bean class="com.bjsxt.interceptor.FirstInterceptor"></bean>
        <!--定义mvc:interceptor标签,代表可配置的局部拦截,可以配置要拦截的路径或不拦截的路径-->
        <mvc:interceptor>
            <!--要拦截的地址,可以使用ant映射方式配置,也就是*或者/** -->
            <mvc:mapping path="/test"/>
            <!--不拦截的地址。可以使用ant映射方式配置-->
            <mvc:exclude-mapping path="/register"/>
            <!--bean标签,当前局部范围内使用的拦截器-->
            <bean class="com.bjsxt.interceptor.SecondInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

【22】过滤器和拦截器区别

1. 来源不同
拦截器是SpringMVC中的技术,过滤器是Java EE中的技术
2. 生效位置不同
拦截器是进入DispatcherServlet后才能执行,过滤器是进入到Servlet容器后就可以触发
3. 目标不同
拦截器拦截的目标是HandlerMethod(控制单元,控制器方法),过滤器可以过滤所有的URL
4. 运行机制不同
拦截器是在HandlerMethod执行前后和视图处理完成后执行,分为三部分。过滤器只能在目标资源前后执行
5. 接口中方法类型不同
拦截器中的方法都是default方法,可以重写也可以不重写。过滤器中的方法都是abstract方法,如果当前类不是抽象类,必须重写
6. 上下文不同
拦截器可以获取到Spring容器中内容,但是Filter因为是被Tomcat管理,所以无法直接获取Spring容器内容,需要通过WebApplicationContextUtils.findApplicationContext(request.getServletContext())间接获取

【23】异常处理@ExceptionHandler

/**
* 局部处理
* 配置在@Controller的控制器类中,只有当前这个控制器类的控制单元出现异常时才能执行,其他类的控制单元出现异常不能执行.
* 每个控制器类中可以有多个处理异常的方法。每个方法上面只需要有@ExceptionHandler,千万别添加了@RequestMapping注解
*/
@Controller
public class DemoController {
    @RequestMapping("/demo")
    @ResponseBody
    public String demo2(){
        Object obj = null;
        obj.toString();
        return "demo2";
    }
    /**
     * 局部异常处理:针对性的。在当前的控制器中定义处理异常的方法。并使用注解修饰即可。
     * 在当前类型中定义方法。要求公开,返回值类型不限,方法名不限,参数就是要处理的异常类型。
     * 增加注解ExceptionHandler,注解属性value,配置Class[],代表这个方法处理哪些类型的异常。
     * 当控制方法抛出异常时, 精确匹配 > 就近匹配 > 视图显示500错误
     */
    @ExceptionHandler(value = ArithmeticException.class)
    public String myexception(){
        System.out.println("Demo-1");
        return "forward:/exception.jsp";
    }

    @ExceptionHandler(value = Exception.class)
    public String myexception2(){
        System.out.println("Demo-2");
        return "forward:/exception2.jsp";
    }
}
package com.bjsxt.advice;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/**
 * 全局异常处理的类型。
 * 需要用注解修饰类型。
 * ControllerAdvice - 当前类型,是用于全局异常处理的类型。
 * 内部定义若干方法,处理不同的异常,定义要求和方式,于局部异常处理一样。
 */
//@ControllerAdvice
public class MyControllerAdvice {
    @ExceptionHandler({Exception.class})
    public String handleEx(Exception e) {
        System.out.println(e.getMessage());
        System.out.println("Exception异常处理");
        return "/errors/error4.jsp";
    }

    @ExceptionHandler({ArrayIndexOutOfBoundsException.class})
    public String handleArrayIndexOutOfBounds(ArrayIndexOutOfBoundsException e) {
        System.out.println(e.getMessage());
        System.out.println("ArrayIndexOutOfBoundsException异常处理");
        return "/errors/error3.jsp";
    }

    @ExceptionHandler({ArithmeticException.class})
    public String handleArithmetic(ArithmeticException e) {
        System.out.println(e.getMessage());
        System.out.println("ArithmeticException异常处理");
        return "/errors/error2.jsp";
    }

    @ExceptionHandler({RuntimeException.class})
    public String handleRuntime(RuntimeException e) {
        System.out.println(e.getMessage());
        System.out.println("运行时异常处理");
        return "/errors/error1.jsp";
    }
}
<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd"
       default-autowire="default">

    <context:component-scan base-package="com.bjsxt.controller, com.bjsxt.advice"></context:component-scan>
</beans>

【24】异常处理-配置文件配置

在Spring MVC中包含HandlerExceptionResolver组件,专门负责处理异常的。接口中只包含一个resolveException方法,程序员可以自行对接口实现,也可以使用Spring MVC提供的实现。
其中最简单好用的就是SimpleMappingExceptionResolver,里面有个全局属性exceptionMappings,表示当出现了什么类型异常时跳转到指定的页面
想要在异常出现时跳转到指定页面,只需要在springmvc.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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd"
       default-autowire="default">

    <context:component-scan base-package="com.bjsxt.controller, com.bjsxt.advice"></context:component-scan>

    <mvc:annotation-driven></mvc:annotation-driven>

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

    <!-- 配置bean对象,实现异常映射处理 -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappin0gs">
            <props>
                <!-- <prop key="异常类型">映射地址</prop> -->
                <prop key="java.lang.Exception">/errors/error4.jsp</prop>
                <prop key="java.lang.ArrayIndexOutOfBoundsException">/errors/error3.jsp</prop>
                <prop key="java.lang.ArithmeticException">/errors/error2.jsp</prop>
                <prop key="java.lang.RuntimeException">/errors/error1.jsp</prop>
            </props>
        </property>
    </bean>

</beans>
上面的方式只有在Spring MVC中出现异常时才会触发,也可以使用Java EE中的配置方式。在web.xml中配置error-page即可
这种在web.xml配置的方式是针对整个项目出现的异常。而在springmvc.xml配置文件的配置方式只是针对Spring MVC框架出现的异常
在Spring MVC框架中没有提供根据状态码跳转到特定的视图。想要实现根据状态码跳转到指定页面可以使用Java EE中提供的实现方案。在web.xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <filter>
        <filter-name>charset</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>charset</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 异常处理,状态码匹配 -->
    <error-page>
        <error-code>500</error-code>
        <location>/errors/error4.jsp</location>
    </error-page>

    <!-- 异常处理,类型匹配 tomcat8插件问题。有Exception类型异常处理时,其他异常处理被屏蔽。 -->
    <!--<error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/errors/error4.jsp</location>
    </error-page>-->
    <!--<error-page>
        <exception-type>java.lang.RuntimeException</exception-type>
        <location>/errors/error1.jsp</location>
    </error-page>
    <error-page>
        <exception-type>java.lang.ArithmeticException</exception-type>
        <location>/errors/error2.jsp</location>
    </error-page>
    <error-page>
        <exception-type>java.lang.ArrayIndexOutOfBoundsException</exception-type>
        <location>/errors/error3.jsp</location>
    </error-page>-->
    
</web-app>

【25】JSR

JSR
JCP(Java Community Process)是一个开发的国际组织,里面包含了一些Java开发者和其他被允许加入的成员。JCP组织主要负责对Java社区进行发展和更新。维护的规范包含:J2ME、J2SE、J2EE、XML等相关规范。组织成员可以提交JSR(Java Specification Requests,Java 规范提案),待组织成员通过提案后会把相关内容加入到下一个版本的规范中

JSR 303
JSR每个提案都带有数字名称。例如JSR 107、JSR 303等。一定要注意的是,对于JSR提案并不是数字越大就需要包含前面内容。例如JSR 107主要是对缓存的提案、JSR 303是对数据校验的提案,这两个提案不存在包含和被包含的关系,也不存在版本先后的关系。
JSR 303是Java EE 6规范的子规范。叫做Bean Validation。这些规范都是注解。各大公司可以针对这些规范做具体实现。在Java开发中使用的最多的JSR 303具体实现就是Hibernate框架中Hibernate-Validator。它对JSR 303的所有约定(constraint)都做了实现,同时还进行了一定的扩充。
  • Hibernate Validator对JSR 303具体实现
注解含义
@AssertFalse类型必须是布尔,取值必须为false
@AssertTrue类型必须是布尔,取值必须为true
@DecimalMax(“3”)最大值为3,value属性是String类型。
@DecimalMin(“1”)最小值为1,value属性是String类型。
@Digits(integer = 10,fraction = 3)integer:整数位最大长度,fraction小数位最大长度
@Email必须是邮箱地址。只要中间包含@,且@前后都具有超过1位的字符就能通过校验。字符可以是数字字母下划线
@Future类型必须是时间类型,允许为null,如果设置值必须是一个将来的时间
@FutureOrPresent类型必须是时间类型,允许为null,如果设置值必须是一个将来或现在的时间(精确到秒)
@Max(5)最大值为5,value属性是long类型
@Min(1)最小值为1,value属性是long类型。
@Negative必须是负数,对数据类型没有要求。
@NegativeOrZero必须是负数或零,对数据类型没有要求。
@NotBlank用在String类型。不能是空白(null和"")
@NotEmpty用在String类型。不能是空白(null和"")
@NotNull不能为null,可以是""。可以用在所有类型中。对于八大基本数据类型来说,永远不为null。
@Null必须为Null。可以用在所有类型中。对于八大基本数据类型来说,永远不可能为null。
@Past类型必须是时间类型,必须是一个过去的时间。精确到秒。
@PastOrPresent类型必须是时间类型,必须是一个过去的时间或现在的时间。精确到秒。·
@Pattern(regexp = “\w{1,6}”)必须满足正则表达式。regexp是必有属性。
@Positive必须是正数,对数据类型没有要求。
@PositiveOrZero必须是正数或零,对数据类型没有要求。
@Size(min = 1,max = 10)用在String类型。个数必须在1和10之间

Hibernate-Validator额外补充

注解含义
@Length(min = 1,max = 10)用在String类型。长度需要在1和10之间
@Range(min = 1,max = 10)数据类型没有要求。取值范围需要在1和10之间
@URL(port = 8080,host = “127.0.0.1”,protocol = “https”)需要是一个合法的URL。默认情况下只要是以http:开头即可。可以通过port限制端口、host限制主机名、protocol限制协议

【26】数据校验

MVC中数据校验包含两种:(1)基于注解方式(2)自定义校验类的方式
  • 注解方式实现
除了项目正常的依赖以外,额外需要导入Hibernate-validator依赖。
注意:对于Tomcat8插件来说,最高支持到6.1.x版本,对于更高的6.2.x或7.x版本是不支持的。导入后启动会报异常。如果希望使用更高版本的依赖,可以使用更高版本Tomcat(本机Tomcat)
<?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>sj125_springmvc</artifactId>
        <groupId>com.bjsxt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sj125_springmvc_4</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.9.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10.8</version>
        </dependency>
        <!-- 文件上传 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.1.7.Final</version>
        </dependency>
        <!--<dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.9.9</version>
        </dependency>-->
    </dependencies>
    <pluginRepositories>
        <pluginRepository>
            <id>mvnrepository</id>
            <url>https://artifacts.alfresco.com/nexus/content/repositories/public/</url>
        </pluginRepository>
    </pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat8-maven-plugin</artifactId>
                <version>3.0-r1756463</version>
                <configuration>
                    <port>80</port>
                    <path>/</path>
                </configuration>

            </plugin>
        </plugins>
    </build>
</project>
package com.bjsxt.pojo;

import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Objects;

/**
 * 实体
 * 校验注解。 Hibernate-Validator注解,定义在实体类型中。
 * 或定义在数据域模型中。
 * 域模型: dto(service和controller互传), vo(控制器给视图传数据或视图给控制器传数据), po, domain, entity, pojo等。
 * 泛指保存数据的类型。 不定指实体类型。
 * <p>
 * 重点掌握。
 */
public class User {
    // 主键非空
    @NotNull(message = "主键必须存在")
    private Integer id;
    // 名字不能是空或空字符串
    @NotBlank
    private String name;
    // 年龄0~160之间
    @Range(min = 0L, max = 160L)
    private int age;
    // 格式必须符合Email地址格式
    @Email
    private String email;

    public User() {
    }

    @Override
    public String toString() {
        return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", email='" + email + '\'' + '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(email, user.email);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, email);
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}
package com.bjsxt.controller;

import com.bjsxt.pojo.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
public class UserController {
    /**
     * 新增用户,参数必须校验。 必须使用注解修饰方法参数。开启校验
     * 默认校验注解无效。
     *
     * @param user
     * @return
     */
    @GetMapping("/addUser")
    public User addUser(@Valid User user) {
        System.out.println(user);
        return user;
    }
}
  • 自定义校验类的方式
package com.bjsxt.pojo;

import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Objects;

/**
 * 实体
 */
public class People {
    // 主键非空
    private Integer id;
    // 名字不能是空或空字符串
    private String name;
    // 年龄0~160之间
    private int age;
    // 格式必须符合Email地址格式
    private String email;

    public People() {
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", email='" + email + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        People user = (People) o;
        return age == user.age &&
                Objects.equals(id, user.id) &&
                Objects.equals(name, user.name) &&
                Objects.equals(email, user.email);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, email);
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}
package com.bjsxt.controller;

import com.bjsxt.pojo.People;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
public class PeopleController {
    @RequestMapping("/addPeople")
    public People addPeople(@Valid People people){
        System.out.println(people);
        return people;
    }
}

package com.bjsxt.valid;

import com.bjsxt.pojo.People;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

/**
 * 自定义校验工具
 * 了解。
 */

public class MyValidator implements Validator {
    /**
     * 当对象是什么类型的时候,需要校验。
     *
     * @param clazz - 要校验的数据对象的类型
     * @return true - 需要校验,运行方法validate。 false - 不校验,不运行方法validate。
     */
    @Override
    public boolean supports(Class<?> clazz) {
        if (People.class == clazz) {
            // 数据对象是People类型,需要校验
            return true;
        }
        return false;
    }

    /**
     * 校验
     *
     * @param target - 被校验的数据对象, People对象
     * @param errors - 如果有校验错误,错误信息,保存在这个参数对象中。
     */
    @Override
    public void validate(Object target, Errors errors) {
        // 校验属性name非空且不是空字符串
        ValidationUtils.rejectIfEmpty(errors, "name", "名字不能为空");
        Object idObj = errors.getFieldValue("id");
        if (idObj == null) {
            // id属性是null。
            errors.rejectValue("id", "null", "主键非空");
        }
        Object ageObj = errors.getFieldValue("age");
        if (ageObj != null) {
            // 一定不是null
            int age = Integer.parseInt(ageObj.toString());
            if (age < 0 || age > 160) {
                // 数据范围错误
                errors.rejectValue("age", "range", "年龄在0~160之间");
            }
        }
    }
}

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd"
       default-autowire="default">

    <context:component-scan base-package="com.bjsxt.controller, com.bjsxt.advice"></context:component-scan>

    <!-- validator属性, 引用一个bean对象,且必须是Validator类型的bean对象。 -->
    <bean id="myValidator" class="com.bjsxt.valid.MyValidator"></bean>
    <mvc:annotation-driven validator="myValidator"></mvc:annotation-driven>

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
</beans>

【27】国际化

SpringMVC中对view的支持。
SpringMVC中的国际化处理
 1. 在classpath下,任意目录中定义国际化属性配置文件   properties。
    文件的命名规则是: 任意名字_语言_国家简写.properties
    文件可以有若干同名不同语言国家的配置。
    配置内容是: key=value。 key是视图层要显示对应value的变量名。  value是视图层要显示的文本。
    注意:如果属性配置文件中,包含非ISO-8859-1字符集内的字符,必须设置文件保存字符集为UTF-8。 通过IDEA -> settings实现配置
    native-to-ascii 勾选。 IDEA自动转译成Unicode编码。
 2. 在springmvc配置文件中,增加2个bean对象配置。
    包括读取国际化属性配置文件的bean对象
    实现基于请求头,识别语言国际化的bean对象
 3. 在JSP页面中,使用springmvc框架提供的标签库,读取国际化属性配置文件内容。
    使用标签:
    <%@ taglib prefix="spring" uri=""%>
    <spring:message code="属性配置文件中的key" text="如果key在属性配置文件中不存在,使用当前文本作为默认值"/>
    <!-- 加载属性文件 -->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="suiyi"></property>
    </bean>
    <!-- 默认也是AcceptHeaderLocaleResolver,所以可以不配置-->
    <bean id="localeResovler" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>
# 在src/main/resources中新建属性文件。属性文件语法:`任意名_语言_国家.properties`。例如:中文是zh、英文是en。
# 如果为了更加精确是哪国使用的这个语言,可以在后面添加国家,因为美式英语和英式英语是不一样的。中国:CN、美国是US,国家缩写都是大写的
# 新建 suiyi_zh_CN.properties
bjsxt.username=用户名
bjsxt.password=密码
bjsxt.submit=登录
# 新建suiyi_en_US.properties
bjsxt.username=username
bjsxt.password=password
bjsxt.submit=login
<!-- 在上面使用taglib执行引入标签库 -->
<!-- `<spring:message code="属性文件的key"></spring:message>`根据语言环境负责加载属性文件中key的值 -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="" method="post">
        <spring:message code="bjsxt.username"></spring:message> <input type="text" name="username"/><br/>
        <spring:message code="bjsxt.password"></spring:message> <input type="text" name="password"/><br/>
        <input type="submit" value="<spring:message code="bjsxt.submit"></spring:message>"/>
    </form>
</body>
</html>

【28】TLD文件

TLD文件: 自定义标签库描述文件。描述标签名称,属性名称,各种使用方案的文件。
  如: JSTL中的c标签库。
  查看TLD文件的方式是,ctl+鼠标左键,点击taglib标签的uri属性。

  重点内容:
   1. <short-name> taglib prefix属性值。
   2. <uri> taglib uri属性值。
   3. <tag>标签,一个标签对应一个JSP中可使用的标签。
      3.1 tag <name>子标签, 标签名称
      3.2 tag <attribute>, 对应一个标签的属性
          3.2.1 tag attribute <name> , 属性的名字
          3.2.2 tag attribute <required> , 属性是否是必须存在的。
          3.2.3 tag attribute <rtexprvalue>, 属性是否可以使用表达式赋值
          3.2.4 tag attribute <type>, 属性的类型。
      3.3 tag <tag-class> 标签的实现Java类型。 每个标签对应一个Java类型。 Java类型识别标签,并显示具体视图或处理具体逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值