过滤器、拦截器、监听器的区别与使用
一、拦截器与过滤器的区别
过滤器的配置比较简单,直接实现Filter 接口即可,也可以通过@WebFilter注解实现对特定URL拦截,看到Filter 接口中定义了三个方法。
- init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。
- doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。
- destroy(): 当容器销毁过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次
拦截器它是链式调用,一个应用中可以同时存在多个拦截器Interceptor, 一个请求也可以触发多个拦截器 ,而每个拦截器的调用会依据它的声明顺序依次执行。首先编写一个简单的拦截器处理类,请求的拦截是通过HandlerInterceptor 来实现,看到HandlerInterceptor 接口中也定义了三个方法。
- preHandle() :这个方法将在请求处理之前进行调用。注意:如果该方法的返回值为false,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。
- postHandle():只有在 preHandle()方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。有意思的是:postHandle() 方法被调用的顺序跟 preHandle() 是相反的,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。
- afterCompletion():只有在 preHandle()方法返回值为true 时才会执行。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。
如下图:
过滤器拦截器运行先后步骤:
其中第2步,SpringMVC的机制是由DispaterServlet来分发请求给不同的Controller,其实这一步是在Servlet的service()方法中执行的
二、何时使用拦截器?何时使用过滤器?何时使用监听器
- 具体使用场景
灵活性上说拦截器功能更强大些,Filter能做的事情,他都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的。
三、spring boot如何使用过滤器、拦截器
两种方式的本质都是一样的,都是去FilterRegistrationBean注册自定义Filter
- 方式一:
①、先定义Filter:
import javax.servlet.*; import java.io.IOException; public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// do something 处理request 或responseSystem.out.println("filter1");// 调用filter链中的下一个filterfilterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {} }②、注册自定义Filter
@Configuration public class FilterConfig {@Beanpublic FilterRegistrationBean registrationBean() {FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());filterRegistrationBean.addUrlPatterns("/*");return filterRegistrationBean;} }- 方式二:
①、定义拦截器:
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {System.out.println("postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {System.out.println("afterCompletion");} }②、配置拦截器:
@Configuration public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor());} }③Controller演示:
@RestController public class UController {@GetMapping("/home")public String home(){System.out.println("home");return "myhome";} }四、过滤器拦截器完整例子
@Configuration public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor());registry.addInterceptor(new MyInterceptor2());} } @Order(1) @Component @WebFilter(filterName = "myFilter",urlPatterns = "/*") public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("MyFilter1-----过滤器的init()方法,随着容器的启动进行了初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// do something 处理request 或responseSystem.out.println("filter1");// 调用filter链中的下一个filterfilterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {System.out.println("MyFilter1--------过滤器的destroy()方法,随着容器的关闭而进行");} } @Order(2) @Component @WebFilter(filterName = "myFilter2",urlPatterns = "/*") public class MyFilter2 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("MyFilter2------过滤器的init()方法,随着容器的启动进行了初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// do something 处理request 或responseSystem.out.println("filter2");// 调用filter链中的下一个filterfilterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {System.out.println("MyFilter2-----过滤器的destroy()方法,随着容器的关闭而进行");} } @Order(3) @Component @WebFilter(filterName = "myFilter3",urlPatterns = "/*") public class MyFilter3 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("MyFilter3------过滤器的init()方法,随着容器的启动进行了初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// do something 处理request 或responseSystem.out.println("filter3");// 调用filter链中的下一个filterfilterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {System.out.println("MyFilter3-------过滤器的destroy()方法,随着容器的关闭而进行");} } public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {System.out.println("postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {System.out.println("afterCompletion");} } @Controller public class MyController {@ResponseBody@RequestMapping("myTest")public String test(){System.out.println("controller.method invoked");return "ok";} }order定义filter的优先级,值越小优先级越高,因此运行顺序为
filter3—doFilter—>filter2—doFilter—>filter1—doFilter—>控制器方法—>返回到filter1—>返回到filter2—>返回到filter3注意:@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响。
可以看到只有执行的时候,才会按filter1,filter2,filter3的顺序执行。同时我们注意到:先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。也即是:postHandle() 方法被调用的顺序跟 preHandle() 居然是相反的。如果实际开发中严格要求执行顺序,那就需要特别注意这一点。那为什么会这样呢? 得到答案就只能看源码了,我们要知道controller 中所有的请求都要经过核心组件DispatcherServlet路由,都会执行它的 doDispatch() 方法,而拦截器postHandle()、preHandle()方法便是在其中调用的。
- doDispatch方法
看看两个方法applyPreHandle()、applyPostHandle()具体是如何被调用的,就明白为什么postHandle()、preHandle() 执行顺序是相反的了。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = this.getInterceptors();if(!ObjectUtils.isEmpty(interceptors)) {for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {HandlerInterceptor interceptor = interceptors[i];if(!interceptor.preHandle(request, response, this.handler)) {this.triggerAfterCompletion(request, response, (Exception)null);return false;}}}return true;} void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {HandlerInterceptor[] interceptors = this.getInterceptors();if(!ObjectUtils.isEmpty(interceptors)) {for(int i = interceptors.length - 1; i >= 0; --i) {HandlerInterceptor interceptor = interceptors[i];interceptor.postHandle(request, response, this.handler, mv);}}}发现两个方法中在调用拦截器数组 HandlerInterceptor[] 时,循环的顺序竟然是相反的。。。,导致postHandle()、preHandle() 方法执行的顺序相反。
五、监听器
Java Web开发中的监听器(listener)就是由application、session、request三个对象创建、销毁或者往其中添加、修改、删除属性时自动执行代码的功能组件,如下所示:
- ServletContextListener:对Servlet上下文的创建和销毁进行监听。
- ServletContextAttributeListener:监听Servlet上下文属性的添加、删除和修改。
- HttpSessionListener:对Session的创建和销毁进行监听。
- HttpSessionAttributeListener:对Session对象中属性的添加、删除和修改进行监听。
- HttpSessionBindingListener:监听Http会话中对象的绑定信息。
- HttpSessionActivationListener:监听器监听Http会话的情况。
- ServletRequestListener:对请求对象的初始化和销毁进行监听。
- ServletRequestAttributeListener:对请求对象属性的添加、删除和修改进行监听。
- 新建一个servlet处理
- 场景
很多时候当我们完成某些业务后需要给用户推送相关消息提醒。对于这种非核心业务功能我们可以拿出来,创建一个事件去异步执行,从而实现核心业务和子业务的解耦。
- 实现
定义事件类 Event
创建一个类,继承ApplicationEvent,并重写构造函数。ApplicationEvent是Spring提供的所有应用程序事件扩展类。
public class Event extends ApplicationEvent {private static final long serialVersionUID = 1L;private String msg ;private static final Logger logger=LoggerFactory.getLogger(Event.class);public Event(String msg) {super(msg);this.msg = msg;logger.info("add event success! message: {}", msg);}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;} }创建一个用于监听指定事件的类,需要实现ApplicationListener接口,说明它是一个应用程序事件的监听类。注意这里需要加上@Component注解,将其注入Spring容器中。
@Component public class MyListener implements ApplicationListener<Event>{private static final Logger logger= LoggerFactory.getLogger(MyListener.class);@Overridepublic void onApplicationEvent(Event event) {logger.info("listener get event,sleep 2 second...");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}logger.info("event msg is:{}",event.getMsg());} }- 事件发布
事件发布很简单,只需要使用Spring 提供的ApplicationEventPublisher来发布自定义事件
- 测试
- 异步执行
默认是没有开启异步的,我们需要手动配置开启异步功能,很简单,只需要在配置类上加上@EnableAsync注解就行了,该注解用于声明启用Spring的异步方法执行功能,需要和@Configuration注解一起使用,我们可以直接加在启动类上。然后在监听方法上加上@Async注解,说明当前方法使用异步去执行。
可以发现已经实现了异步功能,主线程为nio-8080-exec-1,监听线程为 task-1。从浏览器反应可以看出,接口直接返回了,并没有等监听线程执行完后才返回。
- 自定义异步线程池
用默认的Spring线程池会有啥问题呢?SimpleAsyncTaskExecutor不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。很有可能导致OOM。创建配置类(加上@Configuration),实现AsyncConfigurer接口,重写Executor方法。这里我们可以将原先配置在启动类上的@EnableAsync注解放到这个类上而不再直接加在主启动类上。
@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer {private static final Logger logger = LoggerFactory.getLogger(AsyncConfig.class);/*** 自定义异步线程池*/@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();//核心线程数taskExecutor.setCorePoolSize(2);//最大线程数taskExecutor.setMaxPoolSize(10);//队列大小taskExecutor.setQueueCapacity(15);//线程名的前缀taskExecutor.setThreadNamePrefix("async-thread-");taskExecutor.initialize();return taskExecutor;}/*** 捕捉IllegalArgumentException异常* @return*/public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new MyAsyncExceptionHandler();}static class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler{@Overridepublic void handleUncaughtException(Throwable throwable, Method method, Object... objects) {logger.info("TASK Exception message - " + throwable.getMessage());logger.info("Method name - " + method.getName());for (Object param : objects) {logger.info("Parameter value - " + param);}}} }六、 过滤器、拦截器、监听器对比
参考文章
参考文章
参考文章
参考文章
参考文章
总结
以上是生活随笔为你收集整理的过滤器、拦截器、监听器的区别与使用的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 移动互联网派生app研究报告
- 下一篇: 为什么需要建设中台?