(04)Spring开发中的3种拦截机制
过滤器、拦截器、切片是java web开发中常用的技术,本篇讲解一下它们的使用方法及三者区别。
一、过滤器(Filter)
首先说明一下过滤器不是Spring提供的,不要被标题误导。它其实是Servlet的变种,是Java EE定义的。
1、实现Filter,统计每一个请求的耗时
@Component public class TimeFilter implements Filter{
@Override public void destroy() { System.out.println("TimeFilter destroy"); } @Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { System.out.println("TimeFilter start"); Long startTime=new Date().getTime(); arg2.doFilter(arg0, arg1); System.out.println("TimeFilter 耗时:"+(new Date().getTime()-startTime)); System.out.println("TimeFilter end"); } @Override public void init(FilterConfig arg0) throws ServletException { System.out.println("TimeFilter init"); } }
@RestController @RequestMapping("/user") public class UserController { @GetMapping(value="/{id}") public User getUserInfo(@PathVariable String id){ System.out.println("进入getUserInfo服务"); User user=new User(); user.setUsername("tom"); return user; } }
public class User { private String id; private String username; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
启动服务,执行初始化方法,控制台输出:TimeFilter init。浏览器访问http://localhost/user/1,打印日志如下:
2、使用FilterRegistrationBean注入Filter
上面的例子中使用@Component注解,将自定义的Filter纳入Spring管理,如果是第三方的Filter通常我们可以在web.xml中配置过滤器,但是Spring Boot项目中没有web.xml,怎样加载第三方的Filter呢?请看下面的例子。
@Configuration public class WebConfig{ @Bean public FilterRegistrationBean<TimeFilter> createTimeFilterBean(){ FilterRegistrationBean<TimeFilter> filterRegistrationBean=new FilterRegistrationBean<TimeFilter>(); TimeFilter timeFilter=new TimeFilter(); filterRegistrationBean.setFilter(timeFilter); List<String> urls=new ArrayList<String>(); urls.add("/*");//哪些路径起作用 filterRegistrationBean.setUrlPatterns(urls); return filterRegistrationBean; } }
现在去掉TimeFilter类上的注解@Component,启动服务进行测试,效果与上面一致。
二、拦截器(Interceptor)
1、实现Interceptor,统计每一个请求的耗时
@Component public class TimeInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("=======afterCompletion 耗时:========"+(new Date().getTime()-((Long)request.getAttribute("startTime")))); System.out.println("Exception is: "+arg3); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3) throws Exception { System.out.println("=======postHandle 耗时:========"+(new Date().getTime()-((Long)request.getAttribute("startTime")))); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("=======preHandle========"); request.setAttribute("startTime",new Date().getTime()); System.out.println(((HandlerMethod)handler).getBean().getClass().getName()); System.out.println(((HandlerMethod)handler).getMethod().getName()); return true; } }
@Configuration public class WebConfig extends WebMvcConfigurerAdapter{ @Autowired private TimeInterceptor timeInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(timeInterceptor); } }
启动服务,浏览器访问http://localhost/user/1,打印日志如下:
preHandle:在controller中业务方法调用之前调用。返回true才执行业务方法。
postHandle:在controller中业务方法调用之后调用,如果抛出了异常,那么不调用该方法。
afterCompletion:最后调用,无论抛不抛异常该方法都调用(如果抛出的异常被自定义异常处理,则不进入该方法)。
2、测试抛出异常的情况
修改UserController中getUserInfo方法
@GetMapping(value="/{id}") public User getUserInfo(@PathVariable String id){ System.out.println("进入getUserInfo服务"); throw new RuntimeException("抛出的异常信息"); }
启动服务,浏览器访问http://localhost/user/1,打印日志如下:
说明:自定义的拦截器不光会拦截自己写的方法,如果url匹配,也会拦截Spring自己的方法。
三、切片(Aspect)
切片实际是Spring核心之一AOP,即我们通常说的面向切片编程。
1、简单介绍AOP
1)切片就是一个类,要把一个类声明为切片需要满足两个元素:一个是切入点(注解);一个是增强(方法)。
2)切入点有两个作用:一是说明在哪些方法上起作用 ;二是说明在什么时候起作用 。
3)方法是起作用时执行的逻辑,即我们为具体功能增强的统一方法。
4)符合切入点声明条件的方法,在执行时会根据切入点的声明在该方法执行之前或之后或抛出异常时去调用增强。
2、演示切片,统计每一个请求的耗时
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
@Aspect @Component public class TimeAspect { @Around("execution(* com.edu.sl.controller.UserController.*(..))") public Object TimeControllerMethod(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("time aspect start"); Object args[]=pjp.getArgs(); for(Object arg:args){ System.out.println("arg is "+arg); } Long startTime=new Date().getTime(); Object object=pjp.proceed();//调被拦截的方法,object就是返回方法的值。 System.out.println("time aspect 用时:"+(new Date().getTime()-startTime)); System.out.println("time aspect end"); return object; } }
启动服务,浏览器访问http://localhost/user/1,打印日志如下:
1)切入点在什么时候起作用是注解名称决定的,Spring提供了5个注解:
@Before:相当于拦截器的preHandle,具体方法执行之前,先调用增强。
@After:具体方法执行之后,再调用增强。
@AfterThrowing:具体方法抛出异常时,调用增强。
@AfterReturning:具体方法返回数据后调用增强。(不常用)
@Around:包围,这种方式覆盖了前面的4种,工作中一般都用Around方式。
2)切入点在哪些方法上起作用是表达式决定的。
"execution(* com.imooc.web.controller.UserController.*(..))":第一个*表示任何返回值,第二个*表示任何方法,..表示任何参数。
该表达式表示的是UserController中任何方法都会满足切入点。 更多表达式写法请到官方文档
四、三种拦截方法的区别和拦截顺序
1、三者区别
过滤器能获取到HTTP的请求和响应,不能获取这个请求是哪个Controller的哪个方法处理的。
拦截器能获取到HTTP的请求和响应和这个请求是哪个Controller的哪个方法处理的。能获取到异常。不能获取拦截的方法中的具体参数值。
切片不能获取到HTTP的请求和响应,能获取这个请求是哪个Controller的哪个方法处理的。能获取拦截的方法中的具体参数值。
2、拦截顺序
拦截顺序:过滤器-->拦截器-->切片,如果抛出异常,过滤器-->拦截器-->异常处理(ControllerAdvice)-->切片
原文地址:https://www.cnblogs.com/javasl/p/12976531.html
- Asp.net 后台添加CSS、JS、Meta标签(帮助类)
- 分享一下cookies操作(增、删、改、查)小经验
- [Silverlight 4 RC]WebBrowserBrush概览
- 一个例子理解C#位移
- WCF后续之旅(3): WCF Service Mode Layer 的中枢—Dispatcher
- silverlight 2 Random 随机数解决方案
- 开发中巧用Enum枚举类型
- Jquery 事件冒泡
- Vue2的单元测试与调试技术
- silverlight beta 2 将在本周末发布.
- [Silverlight 4 RC]WebBrowser概览
- 英文域名chosen.com超22万元易主
- 简单介绍Docker的架构特性与局限
- .NET4.0下网站应用程序用UrlRewriter.dll重写无后缀路径 (在IIS7.5中的配置方法)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- c++STL容器之list容器
- c++STL容器之deque容器
- Web自动化必会「Web基础、元素定位、元素操作、Selenium运行原理、项目实战+框架」
- HTML5 meta viewport参数详解
- 二分查找应该都会,那么二分查找的变体呢?
- TypeScript 实战算法系列(三):实现链表与变相链表
- JavaScript 测试系列实战(一):使用 Jest 和 Enzyme 测试 React 组件
- TypeScript 实战算法系列(二):实现队列与双端队列
- VBA位操作
- VBA编写Ribbon Custom UI编辑器07——写入xml
- Linux下在文件夹所有文件中查找相关内容
- archlinux安装篇(一) 基本系统
- git使用要点
- 七夕 - 程序员表白代码
- Shell流程控制