Springboot基础知识(11)- 定制 Spring MVC、拦截器(Interceptor)


1. 定制 Spring MVC

    Spring Boot 抛弃了传统 xml 配置文件,通过配置类(标注 @Configuration 的类,相当于一个 xml 配置文件)以 JavaBean 形式进行相关配置。
    
    Spring Boot 对 Spring MVC 的自动配置可以满足我们的大部分需求,但是我们也可以通过自定义配置类(标注 @Configuration 的类)并实现 WebMvcConfigurer 接口来定制 Spring MVC 配置,例如拦截器、格式化程序、视图控制器等等。

    WebMvcConfigurer 是一个基于 Java  8 的接口,该接口定义了许多与 Spring MVC 相关的方法,其中大部分方法都是 default 类型的,且都是空实现。因此我们只需要定义一个配置类实现 WebMvcConfigurer 接口,并重写相应的方法便可以定制 Spring MVC 的配置。

 

方法 描述
void configurePathMatch(PathMatchConfigurer configurer) HandlerMappings 路径的匹配规则。
void configureContentNegotiation(ContentNegotiationConfigurer configurer) 内容协商策略(一个请求路径返回多种数据格式)。
void configureAsyncSupport(AsyncSupportConfigurer configurer) 处理异步请求。
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) 这个接口可以实现静态文件可以像 Servlet 一样被访问。
void addFormatters(FormatterRegistry registry) 添加格式化器或者转化器。
void addInterceptors(InterceptorRegistry registry) 添加 Spring MVC 生命周期拦截器,对请求进行拦截处理。
void addResourceHandlers(ResourceHandlerRegistry registry) 添加或修改静态资源(例如图片,js,css 等)映射;
Spring Boot 默认设置的静态资源文件夹就是通过重写该方法设置的。
void addCorsMappings(CorsRegistry registry) 处理跨域请求。
void addViewControllers(ViewControllerRegistry registry) 主要用于实现无业务逻辑跳转,例如主页跳转,简单的请求重定向,错误页跳转等
void configureViewResolvers(ViewResolverRegistry registry) 配置视图解析器,将 Controller 返回的字符串(视图名称),转换为具体的视图进行渲染。
void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) 添加解析器以支持自定义控制器方法参数类型,实现该方法不会覆盖用于解析处理程序方法参数的内置支持;要自定义内置的参数解析支持, 同样可以通过 RequestMappingHandlerAdapter 直接配置 RequestMappingHandlerAdapter 。
void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) 添加处理程序来支持自定义控制器方法返回值类型。使用此选项不会覆盖处理返回值的内置支持;要自定义处理返回值的内置支持,请直接配置 RequestMappingHandlerAdapter。
void configureMessageConverters(List<HttpMessageConverter<?>> converters) 用于配置默认的消息转换器(转换 HTTP 请求和响应)。
void extendMessageConverters(List<HttpMessageConverter<?>> converters) 直接添加消息转换器,会关闭默认的消息转换器列表;实现该方法即可在不关闭默认转换器的起提下,新增一个自定义转换器。
void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) 配置异常解析器。
void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) 扩展或修改默认的异常解析器列表。

 
    在 Spring Boot 项目中,我们可以通过以下 2 种形式定制 Spring MVC:

        扩展 Spring MVC
        全面接管 Spring MVC


    1) 扩展 Spring MVC

        如果 Spring Boot 对 Spring MVC 的自动配置不能满足我们的需要,还可以通过自定义一个 WebMvcConfigurer 类型(实现 WebMvcConfigurer 接口)的配置类(标注 @Configuration,但不标注 @EnableWebMvc 注解的类),来扩展 Spring MVC。
        
        这样不但能够保留 Spring Boot 对 Spring MVC 的自动配置,享受 Spring Boot 自动配置带来的便利,还能额外增加自定义的 Spring MVC 配置。

        示例,在 “ Springboot基础知识(08)- spring-boot-starter-web(Web启动器)” 里 SpringbootWeb 项目 整合 Thymeleaf 模板 的基础上,代码如下。

            (1) 添加 jQuery (https://jquery.com/)

                下载 jQuery 3.6.0 放置到目录 src/main/resources/static/js/jquery-3.6.0.min.js

            (2) 创建 src/main/resources/templates/home.html 文件

复制代码
 1                 <!DOCTYPE html>
 2                 <html lang="en" xmlns:th="http://www.thymeleaf.org">
 3                 <head>
 4                     <meta charset="UTF-8">
 5                     <title>Home</title>
 6                     <script language="javascript" src="/js/jquery-3.6.0.min.js"></script>
 7                 </head>
 8                 <body>
 9 
10                     <h3>Home Page</h3>
11                     <p>jQuery Status: <span id="jquery_status">OFF</span></p>
12 
13                     <script type="text/javascript">
14                         $(document).ready(function() {
15                             console.log("Home page");
16                             $("#jquery_status").html("ON");
17                         });
18                     </script>
19                 </body>
20                 </html>
复制代码


            (3) 创建 src/main/java/com/example/config/ExtendMvcConfigurer.java 文件

复制代码
 1                 package com.example.config;
 2 
 3                 import org.springframework.context.annotation.Configuration;
 4                 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 5                 import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
 6 
 7                 // 实现 WebMvcConfigurer 接口可以来扩展 SpringMVC 的功能
 8                 @Configuration
 9                 public class ExtendMvcConfigurer implements WebMvcConfigurer {
10 
11                     @Override
12                     public void addViewControllers(ViewControllerRegistry registry) {
13 
14                         // 当访问 “/” 或 “/index.html” 时,都直接跳转到 home
15                         registry.addViewController("/").setViewName("home");
16                         registry.addViewController("/index.html").setViewName("home");
17                     }
18 
19                 }
复制代码


            (4) 创建 src/main/java/com/example/controller/IndexController.java 文件

复制代码
 1                 package com.example.controller;
 2 
 3                 import org.springframework.stereotype.Controller;
 4                 import org.springframework.web.bind.annotation.RequestMapping;
 5 
 6                 @Controller
 7                 public class IndexController {
 8                     @RequestMapping("/home")
 9                     public String home() {
10                         return "home";
11                     }
12                 }
复制代码


            访问 http://localhost:9090/   

                Home Page

                jQuery Status: ON    


    2) 完全接管 Spring MVC

        在一些特殊情况下,可能需要抛弃 Spring Boot 对 Spring MVC 的全部自动配置,完全接管 Spring MVC。此时可以自定义一个 WebMvcConfigurer 类型(实现 WebMvcConfigurer 接口)的配置类,并在该类上标注 @EnableWebMvc 注解,来实现完全接管 Spring MVC。

            注:完全接管 Spring MVC 后,Spring Boot 对 Spring MVC 的自动配置将全部失效。
        
        示例,在上文 SpringbootWeb 项目基础上,代码如下。

            修改 src/main/java/com/example/config/ExtendMvcConfigurer.java 文件

复制代码
 1                 package com.example.config;
 2 
 3                 import org.springframework.context.annotation.Configuration;
 4                 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 5                 import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
 6                 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 7 
 8                 // 实现 WebMvcConfigurer 接口可以来扩展 SpringMVC 的功能
 9                 @EnableWebMvc  // 完全接管SpringMVC
10                 @Configuration
11                 public class ExtendMvcConfigurer implements WebMvcConfigurer {
12 
13                     @Override
14                     public void addViewControllers(ViewControllerRegistry registry) {
15 
16                         // 当访问 “/” 或 “/index.html” 时,都直接跳转到 home
17                         registry.addViewController("/").setViewName("home");
18                         registry.addViewController("/index.html").setViewName("home");
19                     }
20 
21                 }
复制代码


            访问 http://localhost:9090/      

                Home Page

                jQuery Status: OFF

            注:Spring Boot 能够访问位于静态资源文件夹中的静态文件,这是在 Spring Boot 对 Spring MVC 的默认自动配置中定义的。@EnableWebMvc 完全接管 Spring MVC 后,Spring Boot 对 Spring MVC 的默认配置都会失效,此时再访问静态资源文件夹中的静态资源(js/jquery-3.6.0.min.js)就会报 404 错误。


2. 拦截器(Interceptor)

    对于拦截器我们并不陌生,无论是 Struts 2 还是 Spring MVC 中都提供了拦截器功能,它可以根据 URL 对请求进行拦截,主要应用于登陆校验、权限验证、乱码解决、性能监控和异常处理等功能上。Spring Boot 同样提供了拦截器功能。

    在 Spring Boot 项目中,使用拦截器功能通常需要以下步骤:

        (1) 定义拦截器;
        (2) 注册拦截器;
        (3) 指定拦截规则(如果是拦截所有,静态资源也会被拦截)。


    1) 定义拦截器

        在 Spring Boot 中定义拦截器十分的简单,只需要创建一个拦截器类,并实现 HandlerInterceptor 接口即可。

        HandlerInterceptor  接口中定义以下 3 个方法,如下表。

方法 描述
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 该方法在控制器处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)  该方法在控制器处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步修改。
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 该方法在视图渲染结束后执行,可以通过此方法实现资源清理、记录日志信息等工作。


        实现 HandlerInterceptor 接口,代码如下:

复制代码
 1             public class LoginInterceptor implements HandlerInterceptor {
 2 
 3                 @Override
 4                 public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
 5                                         Object handler) throws Exception {
 6 
 7                 }
 8 
 9                 @Override
10                 public void postHandle(HttpServletRequest request, HttpServletResponse response,
11                                         Object handler, ModelAndView modelAndView) throws Exception {
12 
13                 }
14 
15                 @Override
16                 public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
17                                             Object handler, Exception ex) throws Exception {
18 
19                 }
20             } 
复制代码

 

    2) 注册拦截器

        创建一个实现了 WebMvcConfigurer 接口的配置类(使用了 @Configuration 注解的类),重写 addInterceptors() 方法,并在该方法中调用 registry.addInterceptor() 方法将自定义的拦截器注册到容器中。

        实现 WebMvcConfigurer 接口,代码如下:

复制代码
 1             // 实现 WebMvcConfigurer 接口可以来扩展 SpringMVC 的功能
 2             @Configuration
 3             public class ExtendMvcConfig implements WebMvcConfigurer {
 4                 ...
 5 
 6                 @Override
 7                 public void addInterceptors(InterceptorRegistry registry) {
 8                     registry.addInterceptor(new LoginInterceptor());
 9                 }
10             }
复制代码

 

    3) 指定拦截规则

        修改 ExtendMvcConfig 配置类中 addInterceptors() 方法的代码,继续指定拦截器的拦截规则,代码如下。

复制代码
 1         @Configuration
 2         public class ExtendMvcConfig implements WebMvcConfigurer {
 3             ...
 4 
 5             @Override
 6             public void addInterceptors(InterceptorRegistry registry) {
 7                 registry.addInterceptor(new LoginInterceptor())
 8                         .addPathPatterns("/**") // 拦截所有请求,包括静态资源文件
 9                         .excludePathPatterns("/", "/index.html", "/user/login", "/user/login/post", "/css/**",
10                             "/images/**", "/js/**"); // 放行登录页,登陆操作,静态资源
11             }
12         }
复制代码


        在指定拦截器拦截规则时,调用了两个方法,这两个方法的说明如下:

            addPathPatterns:该方法用于指定拦截路径,例如拦截路径为 “/**”,表示拦截所有请求,包括对静态资源的请求。
            excludePathPatterns:该方法用于排除拦截路径,即指定不需要被拦截器拦截的请求。

    示例,在 “Spring基础知识(27)- Spring Boot (八)” 里 SpringbootWeb 项目 整合 Thymeleaf 模板 的基础上,代码如下。

        (1) 创建 src/main/resources/templates/login.html 文件

复制代码
 1             <!DOCTYPE html>
 2             <html lang="en" xmlns:th="http://www.thymeleaf.org">
 3             <head>
 4                 <meta charset="UTF-8">
 5                 <title>Login</title>
 6             </head>
 7             <body>
 8 
 9                 <h3>Login</h3>
10 
11                 <p th:text="${msg}"></p>
12 
13                 <form action="/user/login/post" method="POST">
14 
15                     <p>Username: <input type="text" name="username" value="admin" /></p>
16                     <p>Password: <input type="text" name="password" value="123456" /></p>
17 
18                     <p><input type="submit" value="Submit" /></p>
19 
20                 </form>
21 
22             </body>
23             </html>
复制代码


        (2) 创建 src/main/java/com/example/interceptor/LoginInterceptor.java 文件

复制代码
 1             package com.example.interceptor;
 2 
 3             import javax.servlet.http.HttpServletRequest;
 4             import javax.servlet.http.HttpServletResponse;
 5             import org.springframework.web.servlet.HandlerInterceptor;
 6             import org.springframework.web.servlet.ModelAndView;
 7 
 8             public class LoginInterceptor implements HandlerInterceptor {
 9 
10                 @Override
11                 public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
12                                         Object handler) throws Exception {
13                     Object loginUser = request.getSession().getAttribute("loginUser");
14                     if (loginUser == null) {
15                         request.setAttribute("msg", "No authentication, try login");
16                         request.getRequestDispatcher("/index.html").forward(request, response);
17                         return false;
18                     } else {
19                         return true;
20                     }
21                 }
22 
23                 @Override
24                 public void postHandle(HttpServletRequest request, HttpServletResponse response,
25                                     Object handler, ModelAndView modelAndView) throws Exception {
26                     System.out.println("LoginInterceptor -> postHandle(): modelAndView = " + modelAndView);
27                 }
28 
29                 @Override
30                 public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
31                                             Object handler, Exception ex) throws Exception {
32                     System.out.println("LoginInterceptor -> afterCompletion()");
33                 }
34             }
复制代码


        (3) 创建 src/main/java/com/example/config/ExtendMvcConfigurer.java 文件

复制代码
 1             package com.example.config;
 2 
 3             import org.springframework.context.annotation.Configuration;
 4             import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 5             import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 6 
 7             // 实现 WebMvcConfigurer 接口可以来扩展 SpringMVC 的功能
 8             @Configuration
 9             public class ExtendMvcConfigurer implements WebMvcConfigurer {
10 
11                 @Override
12                 public void addViewControllers(InterceptorRegistry registry) {
13 
14                     // 当访问 “/” 或 “/index.html” 时,都直接跳转到登陆页面
15                     registry.addViewController("/").setViewName("login");
16                     registry.addViewController("/index.html").setViewName("login");
17                     registry.addViewController("/home.html").setViewName("home");
18                 }
19 
20                 @Override
21                 public void addInterceptors(InterceptorRegistry registry) {
22 
23                     registry.addInterceptor(new LoginInterceptor())
24                             .addPathPatterns("/**") // 拦截所有请求,包括静态资源文件
25                             .excludePathPatterns("/", "/index.html", "/user/login", "/user/login/post",
26                                     "/css/**", "/images/**", "/js/**"); // 放行登录页,登陆操作,静态资源
27                 }                
28 
29             }
复制代码


        (4) 创建 src/main/java/com/example/controller/UserController.java 文件

复制代码
 1             package com.example.controller;
 2 
 3             import java.util.Map;
 4             import javax.servlet.http.HttpSession;
 5             import org.springframework.stereotype.Controller;
 6             import org.springframework.web.bind.annotation.RequestMapping;
 7             import org.springframework.web.bind.annotation.RequestMethod;
 8 
 9             @Controller
10             @RequestMapping("/user")
11             public class UserController {
12 
13                 @RequestMapping("/login")
14                 public String login() {
15                     return "login";
16                 }
17 
18                 @RequestMapping(value = "/login/post")
19                 public String loginPost(String username, String password, 
20                                         Map<String, Object> map, HttpSession session) {
21 
22                     if ("admin".equals(username) && "123456".equals(password)) {
23                         session.setAttribute("loginUser", username);
24                         return "redirect:/home.html";
25                     } else {
26                         map.put("msg", "Invalid username or password");
27                     }
28 
29                     return "login";
30                 }
31             }
复制代码


        访问 http://localhost:9090/ 或 http://localhost:9090/home.html

posted @   垄山小站  阅读(506)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示