Spring MVC

一、Spring MVC子父容器问题

因为Spring MVC属于Spring的子框架,所以Spring MVC中可以使用Spring框架的全部内容。

Spring 官方为Spring MVC专门定义了一个容器,这个容器里面放Spring MVC中全部Bean,且这个容器属于Spring容器的子容器。

有这样的一个规定:Spring MVC子容器可以调用Spring 父容器的全部内容。但是Spring父容器不能调用Spring MVC子容器内容。

 二、Spring MVC环境搭建

Spring MVC 在平时随意可以当成一个独立框架看待,但其本质只是Spring Framework中的spring-webmvc.jar文件,这个jar文件依赖了spring web模块和Spring框架核心功能的5个依赖。所以在只使用Spring MVC框架时需要导入spring-webmvc依赖即可。

引入依赖

    <dependencies>
        <!-- 依赖了Spring框架核心功能的5个依赖以及Spring整合Web的依赖spring-web -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.16</version>
        </dependency>
    </dependencies>

创建springmvc配置文件

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 扫描控制器类,千万不要把service等扫描进来,也千万不要在Spring配置文件扫描控制器类所在包 -->
    <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
    <!-- 让Spring MVC的注解生效 不要引错xsd-->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>
springmvc.xml
复制代码

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">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!-- 参数名称必须叫做:contextConfigLocation。单词和大小写错误都导致配置文件无法正确加载 -->
            <param-name>contextConfigLocation</param-name>
            <!-- springmvc.xml 名称自定义,只要和后面创建的文件名称对应就可以了。 -->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- Tomcat启动立即加载Servlet,而不是等到访问Servlet才去实例化DispatcherServlet -->
        <!-- 配置上的效果:Tomcat启动立即加载Spring MVC框架的配置文件-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!-- /表示除了.jsp结尾的uri,其他的uri都会触发DispatcherServlet。此处不要写成 /* -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
web.xml
复制代码

三、@RequestMapping注解

先在springmvc配置文件中开启注解驱动(不开启也能用,但后续会出现问题,所以直接开启)

    <!--配置注解驱动-->
    <mvc:annotation-driven/>

@RequestMapping注解可以写在控制器类上,也可以写在控制单元方法上。

1. 所有属性

2. value、path 属性

value和path都是定义映射的路径。

单个路径:

@RequestMapping(path="/first")

多个路径:

@RequestMapping(path={"/first4","first5"})

当只有value属性时,可以省略value 但path不能省略。

3. name 属性

name:给控制单元定义一个名称。可以理解name是控制单元的注释。

@RequestMapping(value = "/testName",name = "测试下name属性")

4. method 属性

method属性类型是RequestMethod[],RequestMethod是枚举类型,支持HTTP协议中绝大多数请求类型。

 当设置了method属性后,表示只有指定类型请求方式才能访问这个控制单元方法,其他的请求方式访问时,响应会出现405状态码。

// 请求方式只能是DELETE和POST类型。
@RequestMapping(value = "/testMethod",method = {RequestMethod.DELETE,RequestMethod.POST})

4.1 简写

Spring MVC 框架针对不同请求方式提供了5个专门请求方式的注解

复制代码
​    @PostMapping("/first") 

​        等效于 @RequestMapping(value = "/first",method = RequestMethod.POST)

​    @GetMapping("/first")

​        等效于 @RequestMapping(value = "/first",method = RequestMethod.GET)

​    @DeleteMapping("/first")

​        等效于 @RequestMapping(value = "/first",method = RequestMethod.DELETE)

​    @PutMapping("/first")

​        等效于 @RequestMapping(value = "/first",method = RequestMethod.PUT)

​    @PatchMapping("/first")

​        等效于 @RequestMapping(value = "/first",method = RequestMethod.PATCH)
复制代码

5. params 属性

params属性类型是String[],表示请求中必须包含指定名称的请求参数。

@RequestMapping(value="/testParam",params = {"name"})

表示请求中必须包含参数名为name的参数

如果请求中没有包含指定类型参数,响应会出现400状态码。并且明确提示在实际的请求参数中没有明确设置name参数。

6. headers 属性

headers属性类型是String[],表示请求头中必须包含指定的请求头参数。

@RequestMapping(value="/testHeaders",headers = "Accept")

如果请求头中没有指定的请求头参数,浏览器会报404。

7. consumes 属性

consumers表示处理请求内容(Content-Type)的类型,平时多不设置,由Spring MVC自动判断。

8. produces 属性

produces类型是String[],作用是设置@ResponseBody注解的响应内容类型。且仅当请求头中Accept中包含的值才生效。

@RequestMapping(value="/testProduces",produces = "text/html;charset=utf-8")
@ResponseBody
public String testProduces() throws IOException {
    return "这是中文";
}

注意:

  produces只对@ResponseBody注解生效,对于代码中响应输出流对象PrintWriter是无效的。

四、映射路径

1. 多级路径

可以写多级路径,但要注意返回值加 ‘/ ’ 与不加的区别

加了/ :/代表项目根目录,也就是webapp目录的根目录,就会从项目的根目录找。

不加:相当于./ 即当前目录。  

2. Ant 风格

在Spring MVC中支持Ant风格的映射路径写法。所谓的Ant风格就是支持三种特殊的符号。

符号解释
匹配任意单字符
* 匹配0或者任意数量的字符
** 匹配0或者更多数量的目录

解释说明:

使用Ant的特殊符号时,表示模糊匹配。可能出现客户端发送过来的URL能匹配上多个映射路径,这时的优先级为:

固定值 > ? >*> /**

五、Spring MVC中的转发和重定向

1. 转发和重定向区别

(1)转发为一次请求,tomcat内部跳转。重定向为多次请求,不是tomcat内部跳转。

(2)转发是一次请求,无论服务器内部转发多少次,请求对象都不变。所以转发可以共享请求域的值。同时对于客户端浏览器URL是不变的。

重定向后需要客户端重新发起请求,和重定向之前不是一个请求。所以重定向后不能获取到之前设置在请求域的值。同时客户端浏览器URL是改变的。

(3)转发只能跳转到当前项目内部资源。重定向可以跳转到外部资源。例如:从自己的项目中跳转到百度应该使用重定向。

(4)转发时资源路径如果是绝对路径,第一个 / 表示当前项目根目录。

重定向时资源路径时绝对路径,第一个 / 表示 Tomcat 的 webapps目录,即:当前项目的上层目录。

2. SpringMVC中的转发和重定向

在Spring MVC框架中,默认情况下都使用转发进行寻找资源。

@RequestMapping("/test/test2")
public String test2(){
    return "/first.jsp";
}

在资源路径前面添加forward: 表示转发。因为写不写forward:都是转发

所有上面代码等同于:

@RequestMapping("/test/test2")
public String test2(){
    return "forward:/first.jsp";
}

如果要使用重定向,只能在资源路径最前面明确添加redirect

@RequestMapping("/test/test2")
public String test2(){
    return "redirect:/first.jsp";
}

六、WEB-INF目录资源

Java Web项目中规定:WEB-INF中资源是不允许被客户端直接访问,需要先访问控制器,通过控制器的转发来访问这些资源

1. 访问WEB-INF的资源

@RequestMapping("/showSuiyi")
public String showSuiyi(){
    return "/WEB-INF/page/suiyi.jsp";
}

只能通过转发,因为转发是tomcat服务器内部行为。

重定向不能实现,因为重定向不属于tomcat服务器内部行为,会导致浏览器发起二次请求,但根据规定,不能访问WEB-INF下的资源。

七、视图解析器

1. 视图解析器和视图

Spring MVC的控制单元 支持 ModelAndView、String 等多种类型的返回值,

但无论控单元的返回值是哪种类型,Spring MVC 内部最终都会将它们封装成一个 ModelAndView 对象,它由 model(模型数据)和 view(逻辑视图名)两部分组成,

所以 Spring MVC 需要借助 ViewResolver(视图解析器)将 逻辑视图名解析为真正的 View 视图对象,然后才能响应给客户端展示。

 Spring MVC 的核心理念是将 视图与 数据模型进行解耦,视图技术随场景选择:Thymeleaf、JSP、FreeMarker、Excel 等等。

Spring MVC 定义了ViewResolverView接口:

  1.ViewResolver 视图解析器

      视图解析器用来解析逻辑视图,将其解析成真正的视图对象。

      SpringMVC 提供了一个视图解析器的接口 ViewResolver,所有具体的视图解析器必须实现该接口。

    AbstractCachingViewResolver:抽象类,这种视图解析器会把它曾经解析过的视图保存起来,然后每次要解析视图的时候先从缓存里面找,如果找到了对应的视图就直接返回,如果没有就创      建一个新的视图对象,然后把它放到一个用于缓存的map中,接着再把新建的视图返回。使用这种视图缓存的方式可以把解析视图的性能问题降到最低。

    UrlBasedViewResolver:它是对ViewResolver的一种简单实现,而且继承了AbstractCachingViewResolver,主要就是提供的一种拼接URL的方式来解析视图,它可以让我们通过prefix属性指定一个指定的前缀,通过suffix属性指定一个指定的后缀,然后把返回的逻辑视图名称加上指定的前缀和后缀就是指定的视图URL了。如prefix=/WEB-INF/,suffix=.jsp,返回的视图名称viewName=test,则UrlBasedViewResolver解析出来的视图URL就是/WEB-INF/test.jsp。默认的prefix和suffix都是空串。

    InternalResourceViewResolver:它是URLBasedViewResolver的子类,所以URLBasedViewResolver支持的特性它都支持。在实际应用中InternalResourceViewResolver也是使用的最广泛的一个视图解析器。InternalResourceViewResolver解释为内部资源视图解析器。InternalResourceViewResolver会把返回的视图名称都解析为InternalResourceView对象,InternalResourceView会把Controller处理器方法返回的模型数据都存放到对应的request属性中,然后通过RequestDispatcher在服务器端把请求forword到目标URL

    ThymeleafViewResolver:Thymeleaf视图解析器,映射成一个 Thymeleaf 模板文件。

    FreeMarkerViewResolver:UrlBasedViewResolver的子类。FreeMarkerViewResolver会把Controller处理方法返回的逻辑视图解析为FreeMarkerView。

   2.View视图

    View解决数据在移交给特定视图技术之前的准备工作。视图技术不有Jsp,还有Thymeleaf,freemaker等,当我们使用不同的视图技术的时候,需要我们编写不同的视图代码来实现将数据显示给用户,SpringMVC为了解决这个问题,由ViewResolver来解析单元方法的处理结果,创建对应的View的对象,然后再调用View的实现对象来完成相关的操作。

2. 自定义视图解析器

<!--配置自定义试图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 前缀 -->
    <property name="prefix" value="/WEB-INF/page/"></property>
    <!-- 后缀 -->
    <property name="suffix" value=".jsp"></property>
</bean>

其中 id可以为任意值。

配置了自定义视图解析器后,控制单元中就可以只写页面文件的名字:

@RequestMapping("/showSuiyi")
public String showSuiyi(){
    return "suiyi";// 由视图解析器拼接:prefix + suiyi + suffix。具体:/WEB-INF/page/ + suiyi + .jsp
}

如果不希望跳转到视图,而是转到控制器,需要显式的写上forward 或 redirect,这样就不走视图解析器了

@RequestMapping("/showSuiyi3")
public String showSuiyi3(){
    return "suiyi3";// 跳转到 /WEB-INF/page/suiyi3.jsp
}
@RequestMapping("/demo")
public String demo(){
    return "forward:/showSuiyi3"; // 不走视图解析器了。跳转到/showSuiyi3控制器单元
}

3. 视图解析器执行流程

控制单元执行完成后,将控制单元返回的结果固定封装为ModelAndview对象Model中存储了业务数据,View通常存储视图名。

最终目的: 将Model中的业务数据通过视图渲染到客户端。

以jsp视图为例

1>控制单元执行后,由springmvc将返回的结果封装成ModelAndView对象(viewName为xxx.jsp)

2>获取到modelAndView后,执行render(mv,request,response);

3>在render方法中调用了view = resolveViewName(viewName, mv.getModelInternal() ,locale,request);

4>在resolveviewName方法中, 根据视图名匹配视图解析器并解析出视图对象View

  - 默认的使用的视图解析器: InternalResourceViewResourceView

  -jsp使用的视图对象: InternalResourceView

5>返回视图对象后,调用view.render(mv.getModelInternal(),reguest,response);(使用视图对象完成客户端响应结果的渲染)

实际完成Model中的数据通过视图响应回到客户端
  jsp的InternalResourceView的视图对象渲染时:
    1.将Mode1中的数据存储大请求域对象中
    2.将请求转发到.jsp -> java (获取请求去对象中的数据) -> .class -> 将结果输出到客户端

八、静态资源放行

1. 说明

按照SpringMVC的使用流程,需要在web.xml文件中配置DispatcherServlet的拦截范围,而我们配置的拦截范围为”/”,表示拦截除jsp请求以外的所有请求。这样造成,请求是js,css,图片等静态资源的请求,也会被拦截,调用对应的单元方法来处理请求。但是,我们是一个静态资源的请求,不应该按照普通单元方法请求的流程来处理,而是将对应的静态资源响应给浏览器使用。

2. 方式一:

Spring MVC 支持静态资源配置,当URL满足指定路径要求时不再去找控制单元,而是直接转发到特定路径中静态资源。

<!--配置静态资源放行-->
    <!--mapping:当URI是什么样格式时,不再执行控制器,而是寻找静态资源。 ** 是通配符,表示任意层路径 -->
    <!--location:去哪个目录中寻找静态资源。mapping中**的值是什么,就去location目录中找对应资源-->
    <!--例如URL是http://localhost:8080/bjsxt/js/jquery.js 其中mapping的**就是jquery.js,就会去location的/js/目录中寻找jquery.js -->
<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>
<mvc:resources mapping="/images/**" location="/images/"></mvc:resources>

3. 方式二:

<mvc:default-servlet-handler />

在springMVC配置文件中配置<mvc:default-servlet-handler />后,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。

一般Web应用服务器默认的Servlet名称是"default",因此DefaultServletHttpRequestHandler可以找到它。如果你所有的Web应用服务器的默认Servlet名称不是"default",则需要通过default-servlet-name属性显示指定:

<mvc:default-servlet-handler default-servlet-name="所使用的Web服务器默认使用的Servlet名称" />

方式二转自https://www.cnblogs.com/dflmg/p/6393416.html

注意:

  配置了静态资源放行一定要开启注解驱动,否则前端请求不到控制器,都是404

九、控制单元的方法参数

1. 写法

控制单元方法参数一共有两种写法:

(1)紧耦方式。获取原生Servlet API,通过原生Servlet API获取请求参数、设置响应内容、设置作用域的值。

(2)解耦方式。使用Spring MVC提供的方式获取请求参数、设置响应内容、设置作用域的值。

2. 紧耦方式

在Spring MVC中,可以直接在控制单元的方法参数中按需注入HttpServletRequest、HttpServletResponse、HttpSession对象。

注入后使用方式等同于servlet中的使用。

3. 解耦方式

解耦方式是Spring MVC独有方式。是Spring MVC给开发者提供的:

(1)获取请求中内容

(2)设置作用域值

(3)设置响应内容

等写法。

3.1 获取请求中内容

请求参数根据参数的类型分为多种写法。

(1)普通表单参数

(2)多个同名参数

(3)日期类型数据

(4)请求头数据

(5)请求体数据

3.1.1 获取普通表单参数

获取普通表单参数,只需要包含在控制单元中提供与请求参数同名的方法参数即可。

Spring MVC会自动进行类型转换

@RequestMapping("/testParam")
public String testParam(String name,int age){
    System.out.println(name+","+age);
    return "suiyi";
}

参数类型写成int可能会导致java.lang.IllegalStateException

因为如果前端没有传递 age 参数,springmvc在处理参数时会传过来一个null,建议写成Integer

3.1.2 @RequestParam 注解

@RequestParam是方法参数级注解。每个控制单元方法参数前面都能写这个注解。在@RequestParam注解里面提供了四个属性:

这些属性只需要按需设置即可。可以都不使用,也可以都使用,也可以只使用里面的部分。

(1)name属性:当请求参数名和控制单元参数名不对应时,可以使用name指定请求参数名。这样方法参数就可以不与请求参数对应了。

@RequestMapping("/testParam")
public String testParam(@RequestParam(name="name") String name123, Integer age){
    System.out.println(name123+","+age);
    return "suiyi";
}

(2)value属性:是name属性的别名。功能和name属性相同。之所以再次设置一个和name属性相同的value,是因为在Java注解中,当需要设置value属性,且只需要设置value属性时可以省略value属性名,这样写起来更加简单。

@RequestMapping("/testParam")
public String testParam(@RequestParam("name") String name123, Integer age){
    System.out.println(name123+","+age);
    return "suiyi";
}

(3)defaultValue:默认值。表示当请求参数中没有这个参数时给与的默认值。defaultValue类型是String类型,Spring MVC会对值进行类型转换,转换成参数类型。

@RequestMapping("/testParam")
public String testParam(@RequestParam("name") String name123,@RequestParam(defaultValue = "16") Integer age){
    System.out.println(name123+","+age);
    return "suiyi";
}

(4)required:boolean类型,表示请求中是否必须包含参数。

@RequestMapping("/testParam")
public String testParam(@RequestParam("name") String name123,@RequestParam(required = true) Integer age){
    System.out.println(name123+","+age);
    return "suiyi";
}

如果设置为true,且请求中没有这个参数,响应时出现400状态码。

3.1.3 使用JavaBean作为参数

当使用类对象作为参数时,要求属性名和参数名对应,类型转换由Spring MVC自动完成。不支持@RequestParam注解。所以需要先建立一个类。且类中必须提供属性的getter和setter方法,因为Spring MVC就是通过getter和setter把请求参数的值设置到类的属性中。

在控制单元中放置一个People类型对象,对象名称没有要求,只需要保证请求参数名和类的属性名相同就可以了。

@RequestMapping("/testBean")
public String testBean(People peo){
    System.out.println(peo);
    return "suiyi";
}

使用这种方式可以用List接收前端传过来的数组(springmvc自动做转换),但单独用List不能直接接收前端的数组

JavaBean和其他参数可以混用,且如果其他参数名和JavaBean中的属性名重复,都会被赋值(“雨露均沾”),但需要注意有时可能希望使用普通属性类型接收参数,但是JavaBean中还存在个同名属性,且这个属性的类型和参数类型不一样。Spring MVC会进行类型转换,如果能够转换没有问题。如果无法转换会出现400。

3.1.4 接收多个同名参数(复选框)

在提交表单数据时,可能在里面包含复选框。当选中多个复选框时会出现多个同名参数。在Spring MVC中可以使用数组和List接收多个同名参数。

①使用数组接收

当使用数组进行接收时,需要数组对象名和请求参数名一致。如果不想一致,可以使用@RequestParam()定义请求参数名。

    @RequestMapping("/testHover")
    public String testHovers(String [] hovers){
        System.out.println(Arrays.toString(hovers));
        return "suiyi";
    }

 

②使用List接收

在使用List进行接收时,必须在参数前面添加@RequestParam注解,注解中内容就是请求参数名,集合名随意。

@RequestMapping("/testHover")
public String testHovers(@RequestParam("hovers") List<String> hovers){
    System.out.println(hovers);
    return "suiyi";
}

3.1.5 接收日期类型参数

如果希望使用Date类型接收客户端传递过来的数据,默认情况下必须保证客户端参数格式和服务器日期格式一致。

类似 2023/10/23 10:55:23 的格式

所以只要保证客户端传递过来的日期是yyyy/MM/dd hh:mm:ss的格式,Spring MVC会自动进行类型转换。其中小时分钟秒可以省略不写。

直接用Date类型接收

@RequestMapping("/testDate")
public String testDate(Date date){
    System.out.println(date.toLocaleString());
    return "suiyi";
}

@DateTimeFormat


有时提交表单的日期数据可能是yyyy-MM-dd格式的,无法被springmvc解析,需要手动指定格式。

可以使用@DateTimeFormat自定义时间格式。

@DateTimeFormat可以写在控制单元Date类型参数之前,也可以写在JavaBean的属性上面(当使用JavaBean作为参数时需要设置)。

@RequestMapping("/testDate")
public String testDate(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date){
    System.out.println(date.toLocaleString());
    return "suiyi";
}

注意:设置了新格式后,默认格式就不可用了。

3.1.6 接收请求头数据

在HTTP协议中,请求头参数会有很多。如果希望接收请求头数据可以使用@RequestHeader进行接收。

在控制单元方法参数中添加对应名称的参数,并在参数前面添加@RequestHeader注解

@RequestMapping("/testHeader")
public String testHeader(@RequestHeader String Accept){
    System.out.println(Accept);
    return "suiyi";
}

上面这种写法中方法参数名和请求参数名完全对应。但是在Java中参数名的命名规范是首字母小写。Spring MVC对于接收请求参数时名称不区分大小写。所以下面的写法也是可以的。

@RequestMapping("/testHeader")
public String testHeader(@RequestHeader String accept){
    System.out.println(accept);
    return "suiyi";
}

当希望去接收多个单词组成的请求参数时(xxx-xxx-xxx形式的),由于java中不能在参数中写“-”,所有需要在@RequestHeader注解中指定要接收的请求参数名,这时方法参数名就没有特殊要求了。

@RequestMapping("/testHeader")
public String testHeader(@RequestHeader("Accept-Encoding") String suiyi){
    System.out.println(suiyi);
    return "suiyi";
}

3.1.7 获取请求体中内容

3.2 设置作用域的值

Spring MVC 中 提供了request作用域的解耦写法。没有提供Session作用域和Application作用域的解耦写法。

也就是说当像给Request作用域设置内容时有两种写法,给Session和Application作用域设置值只有紧耦方式。

3.2.1 使用Model设置请求域的值

只需要在控制单元方法参数中添加Model对象,对象名称任意。

Model是一个接口,里面有很多子接口和实现类。当放置Model接口时,Spring MVC默认注入的是BindingAwareModelMap。

 在Model接口中有很多个方法,里面最常用的有两个addAttribute(String,Object)和addAllAtributes(Map)。

3.2.2 使用Map设置请求域的值

直接在控制单元方法上添加一个Map,然后向Map对象中put值就可以了。

@RequestMapping("/testScope2")
public String testScope2(Map<String,Object> map){
    map.put("name","value");
    return "suiyi";
}

十、Spring MVC中文乱码问题

Tomcat8及之后的版本已经将Get请求乱码进行了处理,不需要额外处理。

Tomcat7默认的接收请求GET方式的是IOS-8859-1,如果希望正确的显示中文,还需要手动把内容转换为UTF-8编码。

SpringMVC提供了字符编码过滤器,只需要在web.xml中配置并设置编码参数即可

复制代码
<!--配置字符编码过滤器-->
<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>
复制代码

 十一、RestFul风格

Rest(Representational State Transfer:表现层状态转移)是一种软件架构风格,其核心是面向资源的一种设计。

何为面向资源,意思是网络上的所有事物都可以抽象为资源,而每个资源都有唯一的资源标识,对资源的操作不应该改变这些标识。

资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。

分别对应 添加、 删除、修改、查询。

示例:

    @RequestMapping("/personInfo/{id}")
    public String getPersonInfo(@PathVariable Integer id, Model model){
        Person personInfo = personService.getPersonInfo(id);
        model.addAttribute("person", personInfo);
        return "/personInfo.jsp";
    }

{id} 对应url中嵌套的参数,参数名一般和方法参数名保持一致,如果方法参数名不同,需要手动指定

使用@PathVariable注解标记的参数代表不再通过前端控制器传值,而是url中嵌套的参数的值

使用restFul风格要注意在请求路径上加前缀,否则能匹配任何请求。

使用RestFul跳转页面

@RequestMapping("/toPage/{page}")
public String showPage(@PathVariable String page){
    return "/WEB-INF/page/"+yemian+".jsp";
}

十二、@ResponseBody注解

@ResponseBody注解是类或方法级注解。

当方法上添加@ResponseBody注解后,控制单元方法返回值将不再被视图解析器进行解析,不会使用转发或重定向。而是把返回值放入到响应流中进行响应。

1. 设置响应内容类型

在使用@ResponseBody注解时,只要返回值类型不是类或Map或List等满足键值对类型。Spring MVC 都会设置响应内容类型为text/html;charset=ISO-8859-1。

text/html;charset=ISO-8859-1中编码是不支持中文的。所以返回值中包含中文,打印在浏览器中会出现乱码。

想要改变@ResonseBody注解的响应内容类型(Content-Type)只能通过@RequestMapping的produces属性进行设置。

@RequestMapping(value="/demo1",produces = "text/html;charset=utf-8")
@ResponseBody
public String demo1() {
    return "中文";
}

2. 自动转换JSON格式字符串

默认情况下只能返回String类型的数据,返回其他类型会出现406状态码。

Spring MVC默认使用Jackson作为JSON转换工具,引入依赖后@ResponseBody注解可以把控制单元返回值自动转换为JSON字符串。主要完成下面几个事情:

(1)判断返回值是否为JavaBean、JavaBean数组、List<JavaBean类型>、Map等满足键值对的类型。

(2)如果满足键值对类型,会使用Jackson把对象转换为JSON字符串,设置到响应流中。同时会设置响应内容类型(Content-Type)为application/json;charset=utf-8

引入依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.10.8</version>
</dependency>

3. 自动转换xml格式

XML格式在一些开放平台上用的比较多。例如:微信里面很多接口都是XML格式。

在Spring MVC中支持把返回值转换为XML文件。如果还是使用jackson-databind依赖,默认只能转换返回值为类类型的控制单元,返回值为List是无法转换为XML的,同时还要求实体类上必须有@XmlRootElement,才能转换。

可以通过jackson-dataformat-xml完成

导入依赖时注意:

(1)不要导入jackson-databind,只导入jackson-dataformat-xml。

(2)jackson-dataformat-xml版本不要太高,和Tomcat8插件不兼容。2.9.9和Spring 5.3.x可以正确兼容。

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.9.9</version>
</dependency>

十三、@RestController注解

用在类上,相当于所有方法都加了@ResponseBody注解,多用于前后端分离。

十四、@RequestBody注解

1. 介绍

@RequestBody注解底层依赖的依然是Jackson工具包,其作用是把客户端传递过来的请求体中JSON或XML数据转换为Map、类、List<类>、List<Map>等类型。

多用在POST类型的请求中。

@RequestBody注解在单体架构项目使用的不是特别多。主要用在分布式项目中多个项目之间传递数据或一些开发平台中(例如微信开发平台接口返回XML数据)

如果希望在单体架构项目中使用@RequestBody注解,需要在客户端中使用Ajax请求,刻意设置请求的内容类型(Content-Type)为JSON或XML。

2. 请求内容类型

在客户端中无论使用的是<form>表单,还是Ajax请求,post请求内容类型都是application/x-www-form-urlencoded,表示普通表单参数。

3. 修改请求内容类型

如果希望修改请求内容类型,可以使用HTML的<form>中enctype属性或使用Ajax中contentType属性进行设置。

注意:<form>的enctype属性一般只有在文件上传时才会修改,所以希望传递特定类型请求参数内容时,都是通过Ajax进行请求。

复制代码
$.ajax({
    url:"testContentType",
    contentType:"application/json",// 修改请求内容类型为JSON
    data:'{"id":1,"name":"张三"}',// 取值两次必须有单引号,没有单引号无效
    type:"post",// 不能是GET类型请求
    success:function (data) {
        console.log(data);
    },
    dataType:"json"
});
复制代码

注意:

(1)contentType:必须设置。常见取值“application/json”或"application/xml"。如果没有设置这个属性,取值默认是application/x-www-form-urlencoded,表示普通表单参数。当设置为"application/json"时,会把data取值设置到请求体中,所以服务端接收参数时就不能按照普通表单参数进行接收。

(2)data:请求参数。必须是JSON字符串类型,不能是JSON格式的对象。因为在JSON中key两次必须有双引号,所以data取值两侧用单引号包含。因为在JavaScript中字符串string类型可以使用单引号包含,也可以使用双引号包含。

(3)type:请求类型不能是get类型,因为get类型没有请求体。常用就是post类型。

小提示:

 

1.因为一个请求只有一个请求体。控制单元参数中绝对不允许出现两个@RequestBody注解。

 

2.因为@RequestBody底层使用Jackson,所以只适用于把请求体数据转换为JavaBean或Map。绝对不能在@RequestBody后面使用String等类型接收请求体内容。也就是说,客户端把JSON或XML设置到请求体,服务端使用JavaBean或Map接收请求体数据时,才能在控制单元参数前面添加@RequestBody注解。

十五、Spring MVC 文件上传

1. 介绍

默认的表单内容类型application/x-www-form-urlencoded不支持传递文件流。

所以需要在<form>的enctype中设置enctype="multipart/form-data"才表示把文件和其他表单参数设置到请求体中。

 

Spring MVC的文件上传是通过MultipartResovler组件实现的。提供了两个具体的实现类

其中CommonsMultipartResovler是我们需要使用的实现类。

底层是对Apache的Commons-Fileupload的封装,让文件上传代码实现起来更加简单。

所以在文件上传时必须在Spring MVC的配置文件中配置CommonsMultipartResovler组件的Bean,同时也得在项目中导入Commons-Fileupload的依赖。

谨记:没有MultipartResovler是无法获取到Content-Type="multipart/form-data"类型的数据,无论是表单域数据还是文件域数据。

 

总结出来,Spring MVC 文件上传有如下几点要求:

(1)客户端:

  (1.1) 请求方式必须是POST

  (1.2)enctype必须为multipart/form-data

(2)服务端:

  (2.1)必须配置MultipartResovler。否则无法解析上传文件的流数据。(<bean>的id值必须叫做multipartResovler)如果没有配置MultipartResovler不仅仅是文件流数据无法解析,连带着其他表单域数据也无法解析。因为文件流数据和表单数据都在请求体中,不解析的话,文件流数据和表单数据都接收不到。

  (2.2)注意文件域的name取值,文件域必须MultipartFile类型接收。且name的取值必须和MultipartFile对象名相同。

2. 文件上传实现流程

① 引入依赖

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

② 配置上传解析器bean

<!-- 文件上传时,必须配置文件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

只有配置了MultipartResovler,Spring MVC 才会解析上传文件流数据。

id必须叫做multipartResovler,叫其他名字无效。

③ 编写方法

我们直接在单元方法上声明形参来接收请求数据即可,普通表单数据还是直接使用键名获取即可。

上传的时候解析后会被存储到MultipartFile对象中,我们声明MultipartFile类型的形参接即可,但是形参名必须和file标签的name属性值一致

MultipartFile是一个接口,接口中存在很多方法。其中:getInputStream()、getOriginalFilename()、transferTo(File)为常用方法。

小提示:

如果客户端就一个文件域使用一个MultipartFile对象接收就可以了。

如果客户端是多个同名的文件域使用MultipartFile 数组接收。

如果客户端是多个不同名的文件域使用多个MultipartFile对象接收就可以了。

3. 限制上传文件类型

如果项目需求中包含必须上传指定类型文件。

例如,只能是.png和.jpg类型的图片。在Commons-Fileupload中没有提供对应的方法,在Spring MVC中也没有提供对应的封装。所以只能自己编写逻辑进行实现。

复制代码
@RequestMapping("/upload")
public String upload(String name, String address, MultipartFile photo,HttpServletRequest request) throws IOException {
    String oldName = photo.getOriginalFilename();
    String suffix = oldName.substring(oldName.lastIndexOf("."));
    // 判断文件扩展名是否正确
    if(suffix.equals(".png")||suffix.equals(".jpg")) {
        if (!photo.isEmpty()) {
            long timeMillis = System.currentTimeMillis();
            Random random = new Random();
            String fileName = timeMillis + "" + random.nextInt(1000);
            String realPath = request.getServletContext().getRealPath("/images");
            System.out.println(realPath);
            photo.transferTo(new File(realPath, fileName + suffix));
        }
    }else{
        // 可以跳转到错误页面,也可以跑出异常
    }
    return "/upload.jsp";
}
限制上传文件类型
复制代码

4. 限制上传文件大小

在CommonsMultipartResolver中提供了setmaxUploadSize(long)方法,表示设置上传文件的大小。单位是字节byte。默认值为-1,表示无限制。

 因为是setter方法,所以可以在配置Bean时直接进行设值注入。下面演示效果为限制上传文件大小为1KB。

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="1024"></property>
</bean>

再次上传文件,当文件大小超出限制,会报FileUploadBase$SizeLimitExceededException异常,其中SizeLimitExceededException是FileUploadBase的内部类。

十六、Spring MVC 文件下载

1. 介绍

文件下载就是把服务器中的资源下载到本地。

但是需要注意的是浏览器本身作为一款软件,能够打开的文件格式比较多。

例如:.html文件、图片文件、.txt文件、.xml文件、.json文件等。当超链接访问的是浏览器本身能打开的资源。浏览器直接打开。这个特点就是响应头参数Content-Disposition控制的,其默认值为inline,表示能打开就打开,不能打开就下载。

Content-Disposition可取值有两个:

(1)inline。直接在浏览器中打开(能打开就打开,不能打开就下载)。

(2)attachment。以附件形式下载。

复制代码
    @RequestMapping("/download")
    public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            File file = new File(req.getServletContext().getRealPath("/images"), filename);
            FileInputStream fis = new FileInputStream(file);
            ServletOutputStream os = response.getOutputStream();
       //springmvc工具类,用于通过输出流将输入流中的内容输出
            IOUtils.copy(fis, os);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
复制代码

2. 下载文件名中文乱码问题

如果文件下载时包含中文名称,需要保证filename=后面的内容是ISO-8859-1编码。如果filename=后面是UTF-8编码且包含中文会乱码。

解决:

设置响应头中文件名的字符编码为ISO-8859-1

复制代码
    @RequestMapping("/download")
    public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
        try {
            String s = new String(filename.getBytes(), "ISO-8859-1");
            // 此处是ISO-8859-1编码的内容
            response.setHeader("Content-Disposition", "attachment;filename=" + s);
            File file = new File(req.getServletContext().getRealPath("/images"), filename);
            FileInputStream fis = new FileInputStream(file);
            ServletOutputStream os = response.getOutputStream();
       //springmvc工具类,用于通过输出流将输入流中的内容输出
            IOUtils.copy(fis, os);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
复制代码

 十七、拦截器

1. 介绍

使用SpringMVC后,只有一个Servlet,也就是DisptcherServlet。

那么,如果我们仍然使用过滤器来完成请求的拦截,由于过滤器是在Servlet之前执行的,就会造成,过滤器会拦截访问DispatcherServlet的所有请求。

这时可以使用拦截器进行对控制单元方法的拦截,拦截器的执行是在DispatcherServlet之后和单元方法之前的。

2. 使用

2.1 创建拦截器实现类

 创建java类实现HandlerInterceptor接口。并实现preHandle、postHandle、afterCompletion三个方法。

复制代码
   public class MyInterceptor implements HandlerInterceptor {    //执行控制单元方法前执行
   @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor.preHandle");
        return fasle;//false 不放行,true 放行
    }
    //执行完控制单元方法后执行
  //用于操作ModelAndView

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor.postHandle");
    }
    //没有@ResponseBody注解,渲染完成后执行
    //有@ResponseBody注解,执行完postHandle就执行
    //总之是在页面中的内容响应完成后执行。    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor.afterCompletion");
    }
}
复制代码

2.2 配置拦截器

要使拦截器生效,必须配置拦截器

① 局部配置

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--配置具体的拦截器的bean及其拦截范围-->
        <mvc:interceptor>
            <mvc:mapping path="/method/test"/><!--配置拦截的单元方法的访问路径,第一个/表示项目根目录-->
            <bean class="com.gsy.interceptor.MyInterceptor"/><!--配置拦截器的bean对象,只在当前mvc:interceptor内有效-->
        </mvc:interceptor>
    </mvc:interceptors>
排除
<mvc:exclude-mapping path=""/>

② 全局配置

全局的拦截器会拦截静态资源!

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--直接配置bean代表全局配置,不用再配置拦截路径-->
        <bean class="com.gsy.interceptor.MyInterceptor"/>
    </mvc:interceptors>

两种可以同时配置。

注意:只有请求的URL匹配到了控制单元,拦截器才能生效。

3. 拦截方法介绍

3.1 preHandle方法

作用:

  执行拦截的方法preHandle(),返回false表示拦截此次请求,返回true表示放行。

执行时机:

  单元方法执行之前。

参数:

  HttpServletRequest request:此次拦截的请求的request对象。

  HttpServletResponse response:此次拦截的请求的response对象。

  Object handler:HandlerMethod类型,存储了拦截的单元方法的method对象。

返回值:

  boolean类型,false表示拦截,true表示放行。

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HandlerMethod hm=(HandlerMethod)handler;//强转为HandlerMethod类型
    Method method = hm.getMethod();//获取此次请求的单元方法的方法对象
    System.out.println(method.getName());
    System.out.println("MyInterceptor.preHandle");
    return false;
}

3.2 postHandle方法

作用:

  执行拦截的方法postHandle(),可以对ModelAndView进行操作,Model数据进行校验,View操作等。

执行时机:

  单元方法执行之后,视图解析器解析渲染视图之前。

参数:

  HttpServletRequest request:此次拦截的请求的request对象。

  HttpServletResponse response:此次拦截的请求的response对象。

  Object handler:HandlerMethod类型,存储了拦截的单元方法的method对象。

  ModelAndView:存储了model和view信息的对象。

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    //重置跳转的资源路径
    String viewName = modelAndView.getViewName();
    //modelAndView.setViewName("forward:/bb.jsp");
    //获取Model对象中存储的流转数据
    Map<String, Object> model = modelAndView.getModel();
    System.out.println("MyInterceptor.postHandle----"+viewName+"-----"+model.get("str"));
}

3.3 afterCompletion

作用:

  执行拦截的方法afterCompletion(),无论是否出现异常都会执行,可以处理异常,清理资源等。

执行时机:

  视图解析器解析渲染视图完成之后。

参数:

  HttpServletRequest request:此次拦截的请求的request对象。

  HttpServletResponse response:此次拦截的请求的response对象。

  Object handler:HandlerMethod类型,存储了拦截的单元方法的method对象。

  Exception:存储异常信息的对象,如果没有异常信息则默认为null。

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

4. 拦截器栈

拦截器栈指多个拦截器。当一个控制单元被多个拦截器拦截时,就形成了拦截器栈。拦截器栈中拦截器有着严格的执行顺序。执行顺序按照配置顺序执行。先配置的优先级更高。

配置拦截器栈分为局部配置和全局配置。

全局配置

<mvc:interceptors>
    <bean class="com.gsy.inteceptor.MyInteceptor"></bean>
    <bean class="com.gsy.inteceptor.MyInteceptor2"></bean>
</mvc:interceptors>

局部配置

因为在<mvc:interceptor>中只能有一个<bean>,所有如果希望使用局部方式配置拦截器栈,需要配置多个<mvc:interceptor>标签。

复制代码
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/show2"/>
        <bean class="com.gsy.inteceptor.MyInteceptor2"></bean>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/show2"/>
        <bean class="com.gsy.inteceptor.MyInteceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>
复制代码

执行顺序:以哪个拦截器开始就以哪个拦截器结束

5. 拦截器和过滤器的区别(面)

1.来源不同

  拦截器是SpringMVC中的技术,过滤器是Java EE中的技术。

2.生效位置不同

  拦截器是进入DispatcherServlet后才能执行,过滤器是进入到Servlet容器后就可以触发。

3.目标不同

  拦截器拦截的目标是HandlerMethod(控制单元,控制器方法),过滤器可以过滤所有的URL。

4.运行机制不同

  拦截器是在HandlerMethod执行前后和视图处理完成后执行,分为三部分。过滤器只能在目标资源前后执行。

5.接口中方法类型不同

  拦截器中的方法都是default方法,可以重写也可以不重写。过滤器中的方法都是abstract方法,如果当前类不是抽象类,必须重写。

6.上下文不同

  拦截器被Spring MVC管理,可以获取到Spring容器中内容。Filter被Tomcat管理,所以无法获取Spring容器内容。

十八、跨域

1. 跨域介绍

项目和项目之前访问的网址或ip:port称为域(domain)。

跨域:当前项目的协议、ip、端口和访问的URL的协议、IP、端口中有一个不同,这种访问就叫跨域。

例如:当前项目http://localhost:8080 访问了 http://127.0.0.1:8080 就是跨域访问。

2. 什么时候有跨域

跨域只发生在Ajax请求中。

Ajax研发之初为了保证安全性,设置默认情况下不允许跨域访问。

3. 跨域的支持

浏览器为了保证Ajax默认不允许跨域实现,浏览器中都有一个同源策略。

同源策略:当使用ajax请求时,不允许跨域访问。只能访问当前域的资源。

4. 解决跨域的方式

解决跨域的方法有很多,常见的解决方案:

  • jsonp : Spring 4的支持。

  • 设置响应头:Spring 5支持。只需要在允许被跨域访问的方法上面添加@CrossOrigin注解即可。

5. @CrossOrigin解释说明

只要在控制单元方法上添加了@CrossOrigin注解后,会在响应头中添加Access-Control-Allow-Origin:*。

Access-Control-Allow-Origin是HTTP协议中允许哪些IP的项目跨域访问,*表示所有IP。

 等效于在响应头中直接添加允许跨域参数:

@RequestMapping("/demo")
@ResponseBody
public String demo(HttpServletResponse response){
    response.setHeader("Access-Control-Allow-Origin","*");
    return "hello";
}

十九、Spring MVC异常处理

1. 局部配置

配置在@Controller的控制器类中,只有当前这个控制器类的控制单元出现异常时才能执行,其他类的控制单元出现异常不能执行。

每个控制器类中可以有多个处理异常的方法。每个方法上面只需要有@ExceptionHandler,千万别添加了@RequestMapping注解。

复制代码
@Controller
public class DemoController {
    @RequestMapping("/demo")
    @ResponseBody
    public String demo2(){
        Object obj = null;
        obj.toString();
        return "demo2";
    }

    @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";
    }
}
复制代码

2. 全局配置

使用@ControllerAdvice

因为@ControllerAdvice已经继承了@Component注解,所以类上只添加这个注解就可以了。

不需要在添加@Controller注解了。

复制代码
@ControllerAdvice
public class MyExceptionController {
    @ExceptionHandler(value = ArithmeticException.class)
    public String myexception(){
        System.out.println("MyException-1");
        return "forward:/exception.jsp";
    }
    @ExceptionHandler(value = Exception.class)
    public String myexception2(){
        System.out.println("MyException-2");
        return "forward:/exception2.jsp";
    }
}
复制代码

 二十、Spring MVC数据校验

1. 基于注解

2.1 JSR介绍

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

2.2 JSR 303介绍

JSR每个提案都带有数字名称。例如:JSR 107、JSR 303等。一定要注意的是,对于JSR提案并不是数字越大就需要包含前面内容。例如JSR 107主要是对缓存的提案、JSR 303是对数据校验的提案,这两个提案不存在包含和被包含的关系,也不存在版本先后的关系。这个和我国政协会议是类似的,第一个政协委员提出的建议叫做建议1、第二个政协委员提出的建议是建议2。建议1建议提高个税起征点、建议2建议国家分配对象。这俩建议是没有关系的。

JSR 303是Java EE 6规范的子规范。叫做Bean Validation。这些规范都是注解。各大公司可以针对这些规范做具体实现。

在Java开发中使用的最多的JSR 303具体实现就是Hibernate框架中Hibernate-Validator。它对JSR 303的所有约定(constraint)都做了实现,同时还进行了一定的扩充。

2.3 Hibernate Validator包含的内容

Hibernate Validator对JSR实现都存在于Validation-api.jar的javax.validation.constraints包中。

里面所有注解都包含message属性,表示校验不通过后日志打印的信息。

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额外补充,这些注解都在Hibernate-validator.jar的org.hibernate.validator.constraints包中。

注解含义
@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限制协议

2.4 基于注解的具体实现

引入依赖

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

在实体属性上添加注解

public class People {
    @NotNull(message = "姓名不能是null")
    @Length(min = 2,max = 6,message = "长度应该是2-6位")
    private String name;
    private String age;
    // 省略Getter和Setter
}

注解放在实体类的属性上面,每个实体类属性都支持配置多个注解,这些注解同时生效。

控制单元

@RequestMapping("/valid")
public String testValid(@Valid People peo){
    System.out.println(peo);
    return "/abc.jsp";
}

控制单元中必须添加@Valid注解,否则校验不生效。

如果配置了日志文件,需要将日志级别设置为WARN。

二一、Spring MVC包含的组件

SpringMVC将Servlet访问的流程整体封装了起来,浏览器发起请求后调用单元方法来处理请求,这个整个过程从接收到请求,到找到单元方法并调用执行,再到响应结果给浏览器都是由不同的组件协同来实现的,有如下组件:

不同的组建在整个运行过程中的具体表现其实就是不同的对象,也就是不同对象之间的相互调用完成了请求的处理,组件的初始化应该在请求来之前就完成的,当服务器启动的时候会先完成对DispactherServlet的初始化创建,在DispatcherServlet被初始化的时候其底层内部也会完成第2到第10个组件的初始化,调用其initStrategies方法来完成

复制代码
 protected void initStrategies(ApplicationContext context) {
    this.initMultipartResolver(context);
    this.initLocaleResolver(context);
    this.initThemeResolver(context);
    this.initHandlerMappings(context);
    this.initHandlerAdapters(context);
    this.initHandlerExceptionResolvers(context);
    this.initRequestToViewNameTranslator(context);
    this.initViewResolvers(context);
    this.initFlashMapManager(context);
}
复制代码

1. 常用组件说明

DispatcherServlet:前端控制器。Spring MVC的入口,也是整个流程控制中心。其他组件由DispatcherServlet统一调度,降低了组件和组件之间的耦合度。

MultipartResovler:多部分处理器。文件上传时需要使用。

LocaleResolver:解决客户端的区域和时区问题。

ThemeResolver:主题解析器。提供自定义布局。

HandlerMapping: 映射处理器。主要负责处理URL,并找到对应的HandlerMethod。简单说就是找@RequestMapping注解中映射路径是否有和URL匹配的。

HandlerAdapter:适配器。负责调用具体的HandlerMethod。

HandlerExceptionResovler:异常处理器。异常处理,根据不同异常返回视图。

RequestToViewNameTranslator:从请求中获取到视图名称。

ViewResovler:视图解析器,负责解析字符串视图名和物理视图文件的。

FlashMapManager:主要用于存储属性的,本质是一个Map。多用在重定向时。FlashMap在重定向之前存储,重定向之后删除。

ModelAndView:模型和视图。Spring MVC中提供的模型和视图接口。

HandlerInterceptor:拦截器。拦截控制器资源的。

二二、Spring MVC 执行原理

 

  1. 客户端向服务端发起请求,Spring MVC总体入口中央调度器DispatcherServlet进行请求分发。

  2. 中央调度器DispatcherServlet把URL交给映射处理器HandlerMapping进行解析URL。

  3. 映射处理器HandlerMapping将请求映射为HandlerExecutionChain处理器执行链

    1. 可以为多个处理器拦截器HandlerInterceptor

    2. 处理器Handler对象(处理Controller)。

  4. 将处理器执行链HandlerExecutionChain返回到中央调度器DispatcherServlet。

  5. DispatcherServlet根据返回的处理器执行链HandlerExecutionChain获得的处理器Handler,根据处理器Handler选择处理器适配器HandlerAdapter。

    1. 执行拦截器的preHandle()方法。

    2. 调用具体的Handler处理器(处理Controller),在填充Handler的入参过程中会执行数据转换、数据格式化、数据验证,调用具体的Controller完成处理功能,并创建ModelAndView对象。

    3. 执行拦截器的postHandle()方法。

  6. 将ModelAndView对象返回到处理器适配器HandlerAdapter。

  7. 处理器适配器HandlerAdapter将ModelAndView对象返回到中央调度器DispatcherServlet。

  8. 中央调度器DispatcherServlet调用视图解析器ViewResolver解析视图。

  9. 将解析的视图View对象返回到中央调度器DispatcherServlet。

  10. 渲染视图,将视图返回到中央调度器DispatcherServlet,执行拦截器afterCompletion()方法。

  11. 中央调度器DispatcherServlet相应回到浏览器。

 

posted @   ygdgg  阅读(30)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示