Java – Spring MVC 快查

SpringMVC简介

Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称( spring-webmvc ),但它通常被称为“Spring MVC”。

SpringMVC的主要运行原理

 

1.DispatcherServlet : SpringMVC提供,我们需要使用web.xml配置使其生效,它是整个流程处理的核心,所有请求都经过它的处理和分发!

2. HandlerMapping : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它内部缓存handler(controller方法)和handler访问路径数据,被DispatcherServlet调用,用于查找路径对应的handler

3. HandlerAdapter : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它可以处理请求参数和处理响应数据数据,每次DispatcherServlet都是通过handlerAdapter间接调用handler,他是handler和DispatcherServlet之间的适配器!

4. Handler : handler又称处理器,他是Controller类内部的方法简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果!

5. ViewResovler : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效!视图解析器主要作用简化模版视图页面查找的,但是需要注意,前后端分离项目,后端只返回JSON数据,不返回页面,那就不需要视图解析器!所以,视图解析器,相对其他的组件不是必须的!

 

配置 Spring MVC

SpringMVC与Servlet的启动关系

SpringMVC 其实是 Servlet 的封装,当用户访问服务器的时候,请求会到达 DispatcherServlet ,由DispatcherServlet 向 HandlerMapping 查找对应的 url 与 controller 方法。

DispatcherServlet 把数据提交给 HandlerAdapter 以整理请求的数据,让HandlerAdapter 去找 controller方法(Handler ),由 Handler 处理业务需求后返回数据给 HandlerAdapter ,HandlerAdapter 把返回的数据整理好发给 DispatcherServlet 并返回给用户

在这期间,DispatcherServlet 、HandlerMapping 、HandlerAdapter 、Handler 都应该被IoC所管理,所以要执行一个Web应用时,这4个组件都应该存放到Spring IoC 中。

 

Servlet 的启动其实是由Tomcat管理,Tomcat 作为Servlet的容器,Servlet运行在Tomcat中,而SpringMVC又运行在Servlet中,因此,我们需要利用Servlet来启动Spring IoC容器。而Servlet是由web.xml来配置启动的,按原本来说,我们应该在web.xml文件中配置DispatcherServlet 作为Servlet的处理类。

但是 SpringMVC 提供了一个更好的方式,可以免除使用 web.xml 来配置SpringIoC 容器,默认让Servlet的处理由DispatcherServlet 处理,并提供  AbstractAnnotationConfigDispatcherServletInitializer 类进行其它必要的组件配置。

在Servlet启动后,会调用 WebApplicationInitializer 接口的 onStartUp 方法,在这个方法中创建了 IoC容器和DispatcherServlet

 

我们通过继承 AbstractAnnotationConfigDispatcherServletInitializer 类的方法,来处理启动时初始化到IoC容器的配置类。

/**
 * 用于替代 web.xml 配置 Servlet 的类
 * SpringMVC 会自动创建一个 IoC 容器,并把配置类引入到IoC中
 */
public class SpringMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMVCConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

其中,getServletConfigClasses 方法则是声明存入IoC的初始化配置类,getServletMappings 方法则是声明请求拦截规则,“/” 表示所有的请求都会被拦截。

接下来我们看看SpringMVCConfig类的代码

@Configuration
@ComponentScan(basePackages = "cn.unsoft")
public class SpringMVCConfig {

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping(){
        return new RequestMappingHandlerMapping();
    }

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
        return new RequestMappingHandlerAdapter();
    }
}

可以看到,在配置类中,我们把 HandlerMapping 和 HandlerAdapter 都存入IoC容器中了。而Handler则通过 @Controller 或 @RestController 注解进入IoC容器中。

至此,DispatcherServlet 、HandlerMapping 、HandlerAdapter 、Handler四个必须的成员都依次存入IoC容器中了。

接下来只要配置 Tomcat 服务器,就可以启动由SpriongMVC驱动的Web项目了

 

SpringMVC 接收数据

访问路径设置 @RequestMapping

@RequestMapping 的作用是注册地址,将handler方法注册到handlerMapping中。

1.请求地址不要求有 / 开头,如 "user/login" "/user/login" "/user/login/" 都可以

2.请求地址可以有多个

3.请求地址支持模糊匹配,* 表示任意一层字符串,** 表示任意层任意字符串

 

@RequestMapping 声明位置,可以放在类上,也可以放在方法上

放在类上,则该请求url串将应用在整个类上。

放在方法上,则该请求会拼接放在类上的@RequestMapping请求url地址

 

@RequestMapping 请求方式指定

在不指定请求方式默认情况下,任意请求方式都被接收。

指定其它请求方式,可指定一个或多个:

public enum RequestMethod {
	GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
}

 

 

单独注解代替 @RequestMapping 请求方式,只能使用在方法上

@GetMapping

@PostMapping

@PutMapping

@DeleteMapping

@PatchMapping

 

Param参数接收

直接接收

前端发送过来的param参数,可以直接进行接收,前提是接收的形参名需要与param传入的参数名一致

/param?name=TZMing&age=18
    @GetMapping("/param")
    public String param(String name, Integer age) {
        return null;
    }

不传递也不会报错。

 

@RequestParam 注解接收

指定任意的请求参数名,可以设置要求必须传递,也可以设置不要求必须传递,同时也可以设置参数默认值。

    @GetMapping("/reqparam")
    public String reqparam(@RequestParam(value = "name",required = true,defaultValue = "admin") String name,Integer age){
        return null;
    }

value:指定接收前端传入的param参数名

required:指定该参数前端必须传入,默认是必须传递,不则会报 400 错误

defaultValue:指定参数的默认值,当指定默认值时,required 必须为 false

 

特殊值接收

一名多值情况:

当param传递了一名多值,如 key=a&key=b&key=c 时:

使用List集合进行接收:

    @GetMapping("/list")
    public String list(@RequestParam("key") List<String> key){
        return null;
    }

注意:集合接收时必须使用@RequestParam,若不加@RequestParam将key对应的一个字符串直接赋值给集合,产生类型异常。

 

 

使用实体对象接收情况:

准备一个对应属性和带有get和set方法的实体类即可。

/object?name=TZMing&age=18

@Data
public class Person {
    private String name;
    private Integer age;
}

    @GetMapping("object")
    public String object(Person person){
        return null;
    }

注意,使用实体类对象接收无法使用 @RequestParam 定义,因此实体对象的成员变量必须与param参数名一致

 

 

@PathVariable 路径参数接收

@PathVariable 所接收的是请求url中的可变参数,使用 { key } 来定其参数名

    @GetMapping("path/{id}")
    public String path(@PathVariable("id") String id){
        return null;
    }

 

Json 参数接收

Java Servlet 默认不支持 JSON 数据,要支持JSON传参数,

1.需要导入json处理的依赖

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

 

 

2.在handlerAdapter中配置Json转化器,添加 @EnableWebMvc 注解

@Configuration
@ComponentScan(basePackages = "cn.unsoft")
@EnableWebMvc // 相当于 handlerAdapter中配置了json转化器
public class SpringMVCConfig { ... }

 

 

@EnableWebMvc 注解说明

@EnableWebMvc注解效果等同于在 XML 配置中,可以使用 <mvc:annotation-driven> 元素!我们来解析<mvc:annotation-driven>对应的解析工作!

让我们来查看下<mvc:annotation-driven>具体的动作!

可以看到 <mvc:annotation-driven> 标签会触发处理类 MvcNamespaceHandler,而MvcNamespaceHandler中就有处理 annotation-driven 动作的代码:

打开 AnnotationDrivenBeanDefinitionParser 类,可以看到相关的处理代码:

class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {

  public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();

  public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();

  static {
    ClassLoader classLoader = AnnotationDrivenBeanDefinitionParser.class.getClassLoader();
    javaxValidationPresent = ClassUtils.isPresent("jakarta.validation.Validator", classLoader);
    romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
    jaxb2Present = ClassUtils.isPresent("jakarta.xml.bind.Binder", classLoader);
    jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
            ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
    jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
    jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
    jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
    gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
  }


  @Override
  @Nullable
  public BeanDefinition parse(Element element, ParserContext context) {
    //handlerMapping加入到ioc容器
    readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);

    //添加jackson转化器
    addRequestBodyAdvice(handlerAdapterDef);
    addResponseBodyAdvice(handlerAdapterDef);

    //handlerAdapter加入到ioc容器
    readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
    return null;
  }

  //具体添加jackson转化对象方法
  protected void addRequestBodyAdvice(RootBeanDefinition beanDef) {
    if (jackson2Present) {
      beanDef.getPropertyValues().add("requestBodyAdvice",
          new RootBeanDefinition(JsonViewRequestBodyAdvice.class));
    }
  }

  protected void addResponseBodyAdvice(RootBeanDefinition beanDef) {
    if (jackson2Present) {
      beanDef.getPropertyValues().add("responseBodyAdvice",
          new RootBeanDefinition(JsonViewResponseBodyAdvice.class));
    }
  }

可以看到,这个处理类中,会创建 handlerMapping 和  handlerAdapter 存入 IoC 容器中,顺便把处理Json数据的类加入到 handlerMapping 和  handlerAdapter 中,上面的代码还能看出,它同时支持google.gson.Gson 和 fasterxml.jackson.databind 等 Json 解析器。

因此,我们启用了 @EnableWebMvc 相当于配置了 Json 解析器的同时,也帮我们把 handlerMapping 和  handlerAdapter 两个一起创建了,于是我们可以省略了手动创建 handlerMapping 和  handlerAdapter 的动作了:

@Configuration
@ComponentScan(basePackages = "cn.unsoft")
@EnableWebMvc // 启用了后,就无需手动创建下面两个处理器了
public class SpringMVCConfig {

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping(){
        return new RequestMappingHandlerMapping();
    }

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
        return new RequestMappingHandlerAdapter();
    }
}

 

通过上面的配置后,SpringMvc就可以处理Json数据了,然后就可以按以下的方法接收Json数据

@RequestBody 所接收的是请求中提交的Json数据。

{
   "name":"张三",
   "age":18
}

 

 

    @PostMapping("/json")
    public String json(@RequestBody Person person){
        return null;
    }

 

接收Cookie值

通过使用 @CookieValue("cookieName") 注解,能获取到请求的Cookie值,SpringMvc会自动把Cookie的值注入给对应的形参中。

    @GetMapping("/cookie")
    public String cookie(@CookieValue("cookieName") String value){
        return null;
    }

 

保存新的Cookie值

通过接收 HttpServletResponse 并向 response 中增加 Cookie 值对象即可。

    @GetMapping("/cookie")
    public String cookie(@CookieValue("cookieName") String value, HttpServletResponse response){
        Cookie cookie = new Cookie("key","value");
        response.addCookie(cookie);
        return null;
    }

 

获取请求头值

通过使用 @RequestHeader("name") 注解,能获取到请求头的值,并注入到形参变量中。

    @GetMapping("/header")
    public String header(@RequestHeader("key") String value){
        return null;
    }

 

其它获取请求原生api变量值

    @Resource
    private ServletContext servletContext;
    
    @GetMapping("/other")
    public String other(HttpServletRequest request,
                        HttpServletResponse response,
                        HttpSession session
                        ){

        ServletContext servletContext1 = request.getServletContext();
        ServletContext servletContext2 = session.getServletContext();
        return null;
    }

其它生生参数接收如下表:

Controller method argument 控制器方法参数 Description
jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse 请求/响应对象
jakarta.servlet.http.HttpSession 强制存在会话。因此,这样的参数永远不会为 null
java.io.InputStream, java.io.Reader 用于访问由 Servlet API 公开的原始请求正文。
java.io.OutputStream, java.io.Writer 用于访问由 Servlet API 公开的原始响应正文。
@PathVariable 接收路径参数注解
@RequestParam 用于访问 Servlet 请求参数,包括多部分文件。参数值将转换为声明的方法参数类型。
@RequestHeader 用于访问请求标头。标头值将转换为声明的方法参数类型。
@CookieValue 用于访问Cookie。Cookie 值将转换为声明的方法参数类型。
@RequestBody 用于访问 HTTP 请求正文。正文内容通过使用 HttpMessageConverter 实现转换为声明的方法参数类型。
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap 共享域对象,并在视图呈现过程中向模板公开。
Errors, BindingResult 验证和数据绑定中的错误信息获取对象!

 

共享域传参数

什么是共享域?

在Controller到页面之间,Controller 需要传递 response 数据到视图中,而Controller 又无法直接把数据直接传递给视图。那么,在Controller 与视图之间,就存在着一个用于存放 response 数据的区域,Controller 把 response 数据先存到共享域中,然后跳转或转发到其它Controller 或视图后,视图或其它Controller 再从共享域中获取 response 数据,则该区域就是共享域。

常见的共享域有哪些?

Request: HttpServletRequest 是针对每一次请求的共享域,该共享域的数据有效期为一个请求。通常用于每一次请求时的数据传递。

Session: HttpSession 是针对一个浏览器一次会话的共享域,该共享域的数据有效期为一个浏览器与服务器一个会话,通常用于每一个账户登陆有效期的数据传递。

ServletContext: ServletContext 是针对整个系统项目中最大的共享域,该共享域的数据有效期为服务后台整个项目的启动周期。

 

Request 共享域的几种配置方法:

请求域中,用的最多的当属Request共享域了,因为每一次的请求都会使用到该共享域,因此,SpringMvc 封装了多种设置共享域数据的方法,方法如下:

1.Servlet 原生的设置共享域方法:使用 HttpServletRequest 设置

    @GetMapping("/requestServlet")
    public String requestServlet(HttpServletRequest request){
        request.setAttribute("key","value");
        return null;
    }

 

2.SpringMvc 提供的设置共享域数据方法一:使用 Model 设置

    @GetMapping("/requestModel")
    public String requestServlet(Model model){
        model.addAttribute("key","value");
        return null;
    }

 

3.SpringMvc 提供的设置共享域数据方法二:使用 ModelMap 设置

    @GetMapping("/requestModelMap")
    public String requestServlet(ModelMap modelMap){
        modelMap.addAttribute("key","value");
        return null;
    }

 

4.SpringMvc 提供的设置共享域数据方法三:使用 Map 设置

    @GetMapping("/requestMap")
    public String requestServlet(Map map){
        map.put("key","value");
        return null;
    }

 

5.SpringMvc 提供的设置共享域数据方法四:使用 ModelAndView 设置

    @GetMapping("/modelAndView")
    public ModelAndView modelAndView(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("key","value");
        modelAndView.setViewName("index");
        return modelAndView;
    }

ModelAndView 对比上面几种方式有所不同的点:

ModelAndView 是用于 JSP 页面技术的共享域数据传递,因此需要设置JSP页面名称,会自动跳转。

ModelAndView 不能用于前后端分离项目,因为它不能只传递 Json 数据。

 

SpringMvc 响应数据

返回 jsp 页面

对于使用混合开发模式的SpringWeb项目,可以通过创建JSP页面进行渲染

1.使用混合开发,Jsp需要使用视图渲染器,除了 Themelaf 渲染器外,还有Java官方的Jsp视图渲染器,这些渲染器应该创建对象并存放到IoC容器中。

2.需要对Jsp页面的前缀和后缀进行设置,SpringMvc 为了方便用户对Jsp的设置,SpringMvc已经对 Jsp 渲染器做了默认的封装和创建,并提供了重写类 WebMvcConfigurer 类,对Jsp 的设置做了封装,我们不需要再重新 new 一个 Jsp 渲染器进行加入IoC和设置,只需要重写 WebMvcConfigurer 类中的配置方法即可。

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        // 配置 jsp 的路径前后缀
        registry.jsp("/WEB-INF/views/",".jsp");
    }

 

 

Controller 请求输出 jsp 页面:

    @GetMapping("jsp")
    public String jsp(){
        System.out.println("jsp页面被访问");
        return "index";
    }

注意,Controller 中不需使用 @ResponseBody 注解

 

 

使用转发

    @GetMapping("forward")
    public String forward(){
        System.out.println("jsp页面被转发");
        return "forward:/index";
    }

 

 

使用重定向

    @GetMapping("redirect")
    public String redirect(){
        System.out.println("jsp页面被重定向");
        return "forward:https://www.baidu.com";
    }

注意:如果是项目下的资源,转发和重定向都一样都是项目下路径!都不需要添加项目根路径

 

 

返回静态资源

通过重写 WebMvcConfigurer 类中的 configureDefaultServletHandling 方法

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

概念:

 

1.DispatcherServlet 接到请求后会向 handlerMapping 查询是否有对应的请求方法,如果 handlerMapping 没有,则直接报404.

2.开启 DefaultServletHandlerConfigurer 即相当于在 DispatcherServlet 下加上一个 DefaultServletHandler ,当 DispatcherServlet 向 handlerMapping 得出404后,会接着查询 DefaultServletHandler ,而 DefaultServletHandler 的执行原理就是简单的 forward 转发到实际url地址。

 

原理(了解):

1.开启 DefaultServletHandler 相当于 XML 中配置 <mvc:default-servlet-handler> 标签,其应用在:

MvcNamespaceHandler 之中

this.registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());

进去我们可以看到,其原理代码:

 

public BeanDefinition parse(Element element, ParserContext parserContext) {
 .......
        RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
 .......
    }

把DefaultServletHttpRequestHandler放到DispatcherServlet 下,其 DefaultServletHttpRequestHandler 的作用如下:

 

    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Assert.state(this.servletContext != null, "No ServletContext set");
        RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
        if (rd == null) {
            throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" + this.defaultServletName + "'");
        } else {
            rd.forward(request, response);
        }
    }

只是简单的转发。

 

 

返回 json 数据

Controller 中使用 @ResponseBody 注解,即可让SpringMvc把返回的数据以Json 的形式传递转换。

概念:通过添加 @ResponseBody 注解后,handlerAdapter 会对返回的 java 对象转为 json 数据并返回给 DispatcherServlet 并返回给用户。

    @GetMapping("json")
    @ResponseBody
    public Person json(){
        Person person = new Person();
        person.setName("TZMing");
        person.setAge(18);
        return person;
    }

@ResponseBody 注解 放到类中,可以使得整个 Controller 的请求都以 Json 方式返回。

 

@RestController 注解可以让 Controller 的 @Controller 和 @ResponseBody 合并为一个注解使用

 

异常处理

SpringMvc 提供使用注解配置声明式异常处理功能。

1.使用注解 @ControllerAdvice 和 @RestControllerAdvice 注解标记用于配置全局异常处理的类。

2.@ControllerAdvice 是可使用于Jsp上的页面跳转的异常处理类

3.使用注解 @ExceptionHandler(class) 注解标记当抛出什么类型的异常时会被处理什么样的异常处理操作。

/**
 * 全局异常处理类
 */
@RestControllerAdvice
public class GlobalExceptionConfig {
    @ExceptionHandler(NullPointerException.class)
    public String nullPointException(NullPointerException e){
        System.out.println("e = " + e);
        return "nullPointException";
    }
}

 

 

 

拦截器 interceptor

拦截器与过滤器的区别

1.过滤器Filter是JavaWeb的Servlet 提供的官方方法,它的拦截时期位于用户发送请求到DispatcherServlet之间的区域,该区域并未进入SpringMvc区域,因此过滤器Filter不能使用IoC中的资源。

2.拦截器interceptor的执行时机是位于SpringMvc内部,所以其运行在IoC容器中,可以使用IoC容器中的对象资源。

3.拦截器interceptor是由SpringMvc提供的拦截方法,它的拦截时期位于三个地方,分别是

  • handlerAdapter 到 handler之间(preHandler)
  • handler 到 handlerAdapter 之间(postHandler)
  • DispatherServlet 到 Servlet 之间(afterCompletion)

4.过滤器Filter与拦截器interceptor的作用都是一样的,就是为了拦推数据的来与回。

配置拦截器

SpringMvc提供了实现拦截器的标准接口 HandlerInterceptor 接口,我们需要实现该接口。

HandlerInterceptor 接口提供三个抽象方法,它们的拦截时机如下:

  • handlerAdapter 到 handler之间(preHandler)
  • handler 到 handlerAdapter 之间(postHandler)
  • DispatherServlet 到 Servlet 之间(afterCompletion)

 

实现代码如下:

@Component
public class MyInterceptor implements HandlerInterceptor {


    /**
     * 
     * @param request 请求体对象
     * @param response 响应体对象
     * @param handler 执行handler方法的所在方法
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    /**
     * 
     * @param request 请求体对象
     * @param response 响应体对象
     * @param handler 执行handler方法的所在方法
     * @param modelAndView 视图与共享域数据对象
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("request = " + request + ", response = " + response + ", handler = " + handler + ", modelAndView = " + modelAndView);
    }

    /**
     * 
     * @param request 请求体对象
     * @param response 响应体对象
     * @param handler 执行handler方法的所在方法
     * @param ex handler 抛出执常时的异常信息
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("request = " + request + ", response = " + response + ", handler = " + handler + ", ex = " + ex);
    }
}

注意:

 

1.preHandle 需要返回 true 或者 false ,如果返回 true 则表示该请求通过拦截,否则为不通过拦截,该请求不会进入下一个环节,也就不会执行 postHandle 和 afterCompletion 了。

2.postHandle 和 afterCompletion 本身没有返回值,因为这两个处理方法已经处理请求处完完成的阶段,不能产生是否拦截的功能,但能处理一些响应数据。

 

加入拦截器到SpringMvc中

SpringMvc 中提供的 WebMvcConfigurer 中提供了把拦截器加入到SpringMvc中的方法

addInterceptors,通过重写该方法,并把我们的拦截器 new 出来加进去即可。

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor());
    }

注意:节接增加拦截器不做任何配置的情况下,默认是对所有的请求都会被拦截处理。

 

 

拦截器参数设置

指定地址拦截

增加拦截器支持链式编程,从而实现具体配置。

addPathPatterns(): 支持匹配的指定地址精准拦截,* 表示一层的任意方法被拦截,** 表示下面多层的任意方法都被拦截。

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/user/*");
    }

 

 

排除拦截

对某些匹配的地址做排除拦截,* 表示一层的任意方法不会被拦截,** 表示下面多层的任意方法都不会被拦截。

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/user/*")
                .excludePathPatterns("/user/xxx");
    }

注意:排除拦截的前提必须是前面增加拦截规则的范围内。

 

 

拦截器的执行顺序

遵循先来后出规律,选增加的拦截器先拦截,出口时,先增加的拦截器后拦截。

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor());
        registry.addInterceptor(new MyInterceptor1());
    }

preHandler: MyInterceptor , MyInterceptor1

postHandle: MyInterceptor1, MyInterceptor

afterCompletion: MyInterceptor1, MyInterceptor

 

参数校验

当我们使用pojo实体类进行接收参数数据时,未免会让前端发送一些不符合我们预期的数据类型,针对这种数据类型的约束,JSR-303 提供了一种参数校验的规范,该规范起初由 hibernate 实现,但SpringMvc 也支持hibernate实现的规范。

配置 @EnableWebMvc后,SpringMVC 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @Validated 注解即可让 SpringMVC 在完成数据绑定后执行数据校验的工作。

1.要使用参数校验,我们需要导入 JSR-303 规范坐标,与 hibernate 坐标。

<!-- 校验注解 -->
<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-web-api</artifactId>
    <version>9.1.0</version>
    <scope>provided</scope>
</dependency>
        
<!-- 校验注解实现-->        
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>8.0.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator-annotation-processor</artifactId>
    <version>8.0.0.Final</version>
</dependency>

 

 

2.在实体类中的成员变量加上对应不同的注解,来对该成员变量实现不同类型的数据类型约束。

详细的约束注解如下:

注解 规则
@Null 标注值必须为 null
@NotNull 标注值不可为 null
@AssertTrue 标注值必须为 true
@AssertFalse 标注值必须为 false
@Min(value) 标注值必须大于或等于 value
@Max(value) 标注值必须小于或等于 value
@DecimalMin(value) 标注值必须大于或等于 value
@DecimalMax(value) 标注值必须小于或等于 value
@Size(max,min) 标注值大小必须在 max 和 min 限定的范围内
@Digits(integer,fratction) 标注值值必须是一个数字,且必须在可接受的范围内
@Past 标注值只能用于日期型,且必须是过去的日期
@Future 标注值只能用于日期型,且必须是将来的日期
@Pattern(value) 标注值必须符合指定的正则表达式

以上是JSR-303规范的类型约束注解,hibernate 除了实现以上的规范外,还另外新增了一些类型约束注解,如下:

注解 规则
@Email 标注值必须是格式正确的 Email 地址
@Length 标注值字符串大小必须在指定的范围内
@NotEmpty 标注值字符串不能是空字符串
@Range 标注值必须在指定的范围内

 

3.接下来在实体类中对不同的成员变量加上约束注册,案例如下:

public class User {
  
    // username 必须不能为空
    @NotBlank
    private String username;

    // 必须最小从10开始 
    @Min(10)
    private int age;

    // 其长度必须大于3位,且小于10位
    @Length(min = 3,max = 10)
    private String name;

    //email 必须邮箱格式
    @Email
    private String email;

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

注意:

 

@NotNull、@NotEmpty、@NotBlank 都是用于在数据校验中检查字段值是否为空的注解,但是它们的用法和校验规则有所不同。

1. @NotNull (包装类型不为null),用于判断对象是否为空时使用

@NotNull 注解是 JSR 303 规范中定义的注解,当被标注的字段值为 null 时,会认为校验失败而抛出异常。该注解不能用于字符串类型的校验,若要对字符串进行校验,应该使用 @NotBlank 或 @NotEmpty 注解。
2. @NotEmpty (集合类型长度大于0)

@NotEmpty 注解同样是 JSR 303 规范中定义的注解,对于 CharSequence、Collection、Map 或者数组对象类型的属性进行校验,校验时会检查该属性是否为 Null 或者 size()==0,如果是的话就会校验失败。但是对于其他类型的属性,该注解无效。需要注意的是只校验空格前后的字符串,如果该字符串中间只有空格,不会被认为是空字符串,校验不会失败。用于判断集合是否没有成员时使用

3. @NotBlank (字符串,不为null,切不为" "字符串)。用于判断字符串是否为空时使用

@NotBlank 注解是 Hibernate Validator 附加的注解,对于字符串类型的属性进行校验,校验时会检查该属性是否为 Null 或 “” 或者只包含空格,如果是的话就会校验失败。需要注意的是,@NotBlank 注解只能用于字符串类型的校验。

总之,这三种注解都是用于校验字段值是否为空的注解,但是其校验规则和用法有所不同。在进行数据校验时,需要根据具体情况选择合适的注解进行校验。

 

4.在Controller 中接收该实体类时,使用 @Validated 注解开启对该实体类的参数校验功能

对出现参数不符合校验要求的,可以在实体类对象旁边增加一个 BindingResult 对象(该对象必须紧挨着校验实体类对象),该对象可以判断接收的参数中是否存在不合规的情况,用户可以自行操作异常抛出。

    @PostMapping("/person")
    public Person person(@Validated @RequestBody Person person, BindingResult result) throws Exception {
        if (result.hasErrors()){
            throw new Exception("参数字段不符合要求");
        }
        return person;
    }

 

 

 

 

如果您喜欢本站,点击这儿不花一分钱捐赠本站

这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看

修改版本安卓软件,加群提示为修改者自留,非本站信息,注意鉴别

THE END
分享
二维码
打赏
海报
Java – Spring MVC 快查
SpringMVC简介 Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称( spring-webmvc ),但它通常被称为“……
<<上一篇
下一篇>>