微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Spring基础知识30- Spring Boot (十一)

定制 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 配置。

        示例,在 “Spring基础知识(27)- Spring Boot (八)” 里 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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐