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
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库