SpringBoot.12.SpringBoot中的拦截器

x33g5p2x  于2022-04-11 转载在 Spring  
字(11.2k)|赞(0)|评价(0)|浏览(461)

前言

拦截器(Interceptor)主要是针对特定处理器进行拦截的。也就是说它拦截的对象是我们的Controller请求。我们可以将Controller中的通用代码进行抽取放在拦截器中,就这一点来讲功能跟我们之前讲的AOP类似(Interceptor底层就是利用AOP来实现的)。在实际开发中Interceptor通常用来做权限校验(现在都有成熟的权限校验框架。比如轻量级的Shiro,还有Spring推出的Spring Security

拦截器

介绍

拦截器(Interceptor)实现对每一个请求处理前后进行相关的业务处理,类似与servlet中的Filter。
SpringMVC 中的 Interceptor 拦截请求是通过 HandlerInterceptor 接口来实现的。

拦截器可以通过以下几种方式进行实现:

  1. 实现 SpringMVC 的 HandlerInterceptor 接口;
  2. 继承实现了 HandlerInterceptor 接口的类,比如 SpringMVC 已经提供的实现了HandlerInterceptor 接口的抽象类 HandlerInterceptorAdapter;
  3. 实现 SpringMVC 的 WebRequestInterceptor 接口;
  4. 继承实现了 WebRequestInterceptor 的类;

具体实现

1.新建项目

在上一节SpringBoot.11.IDEA中如何快速复制当前父项目中的一个Module为新的项目中我们已经准备好了Module – springboot-08-interceptor。如下图所示:

2.MyInterceptor01.java
  1. package com.christy.config.interceptors;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.web.servlet.HandlerInterceptor;
  5. import org.springframework.web.servlet.ModelAndView;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. /**
  9. * 自定义拦截器
  10. * @Author Christy
  11. * @Date 2021/9/28 10:20
  12. **/
  13. public class MyInterceptor01 implements HandlerInterceptor {
  14. private static final Logger log = LoggerFactory.getLogger(MyInterceptor01.class);
  15. /**
  16. *
  17. * @author Christy
  18. * @date 2021/9/28 10:22
  19. * @param request
  20. * @param response
  21. * @param handler 当前请求请求的控制器方法对象 InterceptorController#sayHello
  22. * @return boolean
  23. * true:表示放行当前请求
  24. * false:终端当前请求 例如做权限校验时如果校验不通过我们可以使用response.sendRedirect(request.getContextPath()+"/login.suffix");跳转到登录界面
  25. */
  26. @Override
  27. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  28. log.info("==========MyInterceptor01==========handler=========" + handler);
  29. log.info("==========MyInterceptor01==========preHandle=========");
  30. return true;
  31. }
  32. /**
  33. *
  34. * @author Christy
  35. * @date 2021/9/28 10:28
  36. * @param request
  37. * @param response
  38. * @param handler 当前请求请求的控制器方法对象 InterceptorController#sayHello
  39. * @param modelAndView 模型和视图 当前请求访问方法的 modelandview 对象
  40. * @return void
  41. */
  42. @Override
  43. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  44. log.info("==========MyInterceptor01==========postHandle=========");
  45. }
  46. /**
  47. *
  48. * @author Christy
  49. * @date 2021/9/28 10:29
  50. * @param request
  51. * @param response
  52. * @param handler 当前请求请求的控制器方法对象 InterceptorController#sayHello
  53. * @param ex 如果控制器出现异常时异常对象
  54. * @return void
  55. */
  56. @Override
  57. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  58. log.info("==========MyInterceptor01==========ex========={}", ex);
  59. log.info("==========MyInterceptor01==========afterCompletion=========");
  60. }
  61. }
3.配置拦截器

我们进入到MyWebMvcConfigurer.java中,按快捷键Alt+Insert,选择Implement Methods…或者直接按快捷键Alt+Shift+P。如下图所示:

Select Methods to Implement中选择addIntefceptors()这个方法。如下图所示:

修改MyWebMvcConfigurer.java内容如下所示:

  1. import com.christy.config.interceptors.MyInterceptor01;
  2. import com.christy.constants.FileConstants;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  5. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  6. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  7. /**
  8. * @Author Christy
  9. * @Date 2021/9/15 16:24
  10. **/
  11. @Configuration
  12. public class MyWebMvcConfigurer implements WebMvcConfigurer {
  13. /**
  14. * 配置拦截器
  15. * addInterceptor(): 指定拦截器
  16. * addPathPatterns("/**"):拦截所有Controller请求
  17. * excludePathPatterns("/file/**"):排除文件操作的请求路径
  18. * order():指定拦截器执行顺序 int 类型数字: 默认按照自然排序执行 数字相同时,按照配置顺序执行
  19. */
  20. @Override
  21. public void addInterceptors(InterceptorRegistry registry) {
  22. registry.addInterceptor(new MyInterceptor01())
  23. .addPathPatterns("/interceptor/**")
  24. .excludePathPatterns("/file/**")
  25. .order(1);
  26. }
  27. @Override
  28. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  29. /**
  30. * 文件上传到服务器后并不能在项目中直接访问,需要将磁盘路径映射成虚拟路径使用http://domain/的方式才可以
  31. */
  32. String os = System.getProperty("os.name");
  33. if(os.toLowerCase().startsWith("win")){
  34. // Windows虚拟路径映射本地磁盘
  35. registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileWindowsPath);
  36. } else {
  37. // Linux虚拟路径映射本地磁盘
  38. registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileLinuxPath);
  39. }
  40. }
  41. }
4.InterceptorController.java
  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RestController;
  5. /**
  6. * @Author Christy
  7. * @Date 2021/9/28 10:44
  8. **/
  9. @RestController
  10. @RequestMapping("/interceptor")
  11. public class InterceptorController {
  12. private static final Logger log = LoggerFactory.getLogger(InterceptorController.class);
  13. @RequestMapping("sayHello")
  14. public String sayHello(){
  15. log.info("Hello Interceptor…");
  16. return "interceptor";
  17. }
  18. }
5.测试

启动项目,浏览器访问http://localhost:8808/interceptor/sayHello,注意观察页面和控制台输出:

我们改造一下sayHello()方法抛出一个异常,如下所示:

  1. @RequestMapping("sayHello")
  2. public String sayHello(){
  3. log.info("Hello Interceptor…");
  4. int i = 1/0;
  5. return "interceptor";
  6. }

重新启动项目,浏览器访问http://localhost:8808/interceptor/sayHello,注意观察页面和控制台输出:

多个拦截器的执行顺序

1.MyInterceptor01.java

我们修改一下MyInterceptor01.java如下:

  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. import org.springframework.web.servlet.HandlerInterceptor;
  4. import org.springframework.web.servlet.ModelAndView;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. /**
  8. * 自定义拦截器
  9. * @Author Christy
  10. * @Date 2021/9/28 10:20
  11. **/
  12. public class MyInterceptor01 implements HandlerInterceptor {
  13. private static final Logger log = LoggerFactory.getLogger(MyInterceptor01.class);
  14. @Override
  15. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  16. log.info("==========MyInterceptor01==========1=========");
  17. return true;
  18. }
  19. @Override
  20. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  21. log.info("==========MyInterceptor01==========2=========");
  22. }
  23. @Override
  24. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  25. log.info("==========MyInterceptor01==========3=========");
  26. }
  27. }
2.MyInterceptor02.java
  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. import org.springframework.web.servlet.HandlerInterceptor;
  4. import org.springframework.web.servlet.ModelAndView;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. /**
  8. * 自定义拦截器
  9. * @Author Christy
  10. * @Date 2021/9/28 10:20
  11. **/
  12. public class MyInterceptor02 implements HandlerInterceptor {
  13. private static final Logger log = LoggerFactory.getLogger(MyInterceptor02.class);
  14. @Override
  15. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  16. log.info("==========MyInterceptor02==========4=========");
  17. return true;
  18. }
  19. @Override
  20. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  21. log.info("==========MyInterceptor02==========5=========");
  22. }
  23. @Override
  24. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  25. log.info("==========MyInterceptor02==========6=========");
  26. }
  27. }
3.MyWebMvcConfigurer.java
  1. import com.christy.config.interceptors.MyInterceptor01;
  2. import com.christy.config.interceptors.MyInterceptor02;
  3. import com.christy.constants.FileConstants;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  6. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  7. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  8. /**
  9. * @Author Christy
  10. * @Date 2021/9/15 16:24
  11. **/
  12. @Configuration
  13. public class MyWebMvcConfigurer implements WebMvcConfigurer {
  14. /**
  15. * 配置拦截器
  16. * addInterceptor(): 指定拦截器
  17. * addPathPatterns("/**"):拦截所有Controller请求
  18. * excludePathPatterns("/file/**"):排除文件操作的请求路径
  19. * order():指定拦截器执行顺序 int 类型数字: 默认按照自然排序执行 数字相同时,按照配置顺序执行
  20. */
  21. @Override
  22. public void addInterceptors(InterceptorRegistry registry) {
  23. registry.addInterceptor(new MyInterceptor01())
  24. .addPathPatterns("/interceptor/**")
  25. .excludePathPatterns("/file/**")
  26. .order(1);
  27. registry.addInterceptor(new MyInterceptor02())
  28. .addPathPatterns("/interceptor/**")
  29. .excludePathPatterns("/file/**")
  30. .order(2);
  31. }
  32. @Override
  33. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  34. /**
  35. * 文件上传到服务器后并不能在项目中直接访问,需要将磁盘路径映射成虚拟路径使用http://domain/的方式才可以
  36. */
  37. String os = System.getProperty("os.name");
  38. if(os.toLowerCase().startsWith("win")){
  39. // Windows虚拟路径映射本地磁盘
  40. registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileWindowsPath);
  41. } else {
  42. // Linux虚拟路径映射本地磁盘
  43. registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileLinuxPath);
  44. }
  45. }
  46. }
4.测试

重新启动项目,浏览器访问http://localhost:8808/interceptor/sayHello,注意观察控制台输出:

多个拦截器注册以后按照注册的顺序以式结构存储,上面我们配置了两个拦截器,MyInterceptor01先入栈,MyInterceptor02后入栈;所以当请求来了之后:

首先,进入MyInterceptor01preHandle打印输出1,再进入MyInterceptor02preHandle打印输出4;这是一个入栈的过程

其次访问方法主体打印输出方法体内的logger

然后,进入MyInterceptor02postHandle打印输出5,再进入MyInterceptor01postHandle打印输出2;这是一个出栈的过程

最后,进入MyInterceptor02afterCompletion打印输出6,再进入MyInterceptor01afterCompletion打印输出3;这也是一个出栈的过程

按照上面的描述如果我们更换两个拦截器的顺序,让MyInterceptor02先入栈,MyInterceptor01后入栈;那么打印的结果应该是4->1->2->5->3->6

我们修改MyWebMvcConfigurer.java的代码更换两个拦截器的顺序如下:

  1. import com.christy.config.interceptors.MyInterceptor01;
  2. import com.christy.config.interceptors.MyInterceptor02;
  3. import com.christy.constants.FileConstants;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  6. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  7. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  8. /**
  9. * @Author Christy
  10. * @Date 2021/9/15 16:24
  11. **/
  12. @Configuration
  13. public class MyWebMvcConfigurer implements WebMvcConfigurer {
  14. /**
  15. * 配置拦截器
  16. * addInterceptor(): 指定拦截器
  17. * addPathPatterns("/**"):拦截所有Controller请求
  18. * excludePathPatterns("/file/**"):排除文件操作的请求路径
  19. * order():指定拦截器执行顺序 int 类型数字: 默认按照自然排序执行 数字相同时,按照配置顺序执行
  20. */
  21. @Override
  22. public void addInterceptors(InterceptorRegistry registry) {
  23. registry.addInterceptor(new MyInterceptor01())
  24. .addPathPatterns("/interceptor/**")
  25. .excludePathPatterns("/file/**")
  26. .order(2);
  27. registry.addInterceptor(new MyInterceptor02())
  28. .addPathPatterns("/interceptor/**")
  29. .excludePathPatterns("/file/**")
  30. .order(1);
  31. }
  32. @Override
  33. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  34. /**
  35. * 文件上传到服务器后并不能在项目中直接访问,需要将磁盘路径映射成虚拟路径使用http://domain/的方式才可以
  36. */
  37. String os = System.getProperty("os.name");
  38. if(os.toLowerCase().startsWith("win")){
  39. // Windows虚拟路径映射本地磁盘
  40. registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileWindowsPath);
  41. } else {
  42. // Linux虚拟路径映射本地磁盘
  43. registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileLinuxPath);
  44. }
  45. }
  46. }

然后重启项目,浏览器访问http://localhost:8808/interceptor/sayHello,注意观察控制台输出:

相关文章