过滤器、拦截器、监听器的区别与使用
一、攔截器與過濾器的區(qū)別
過濾器的配置比較簡單,直接實現Filter 接口即可,也可以通過@WebFilter注解實現對特定URL攔截,看到Filter 接口中定義了三個方法。
- init() :該方法在容器啟動初始化過濾器時被調用,它在 Filter 的整個生命周期只會被調用一次。注意:這個方法必須執(zhí)行成功,否則過濾器會不起作用。
- doFilter() :容器中的每一次請求都會調用該方法, FilterChain 用來調用下一個過濾器 Filter。
- destroy(): 當容器銷毀過濾器實例時調用該方法,一般在方法中銷毀或關閉資源,在過濾器 Filter 的整個生命周期也只會被調用一次
攔截器它是鏈式調用,一個應用中可以同時存在多個攔截器Interceptor, 一個請求也可以觸發(fā)多個攔截器 ,而每個攔截器的調用會依據它的聲明順序依次執(zhí)行。首先編寫一個簡單的攔截器處理類,請求的攔截是通過HandlerInterceptor 來實現,看到HandlerInterceptor 接口中也定義了三個方法。
- preHandle() :這個方法將在請求處理之前進行調用。注意:如果該方法的返回值為false,將視為當前請求結束,不僅自身的攔截器會失效,還會導致其他的攔截器也不再執(zhí)行。
- postHandle():只有在 preHandle()方法返回值為true 時才會執(zhí)行。會在Controller 中的方法調用之后,DispatcherServlet 返回渲染視圖之前被調用。有意思的是:postHandle() 方法被調用的順序跟 preHandle() 是相反的,先聲明的攔截器 preHandle() 方法先執(zhí)行,而postHandle()方法反而會后執(zhí)行。
- afterCompletion():只有在 preHandle()方法返回值為true 時才會執(zhí)行。在整個請求結束之后, DispatcherServlet 渲染了對應的視圖之后執(zhí)行。
如下圖:
過濾器攔截器運行先后步驟:
其中第2步,SpringMVC的機制是由DispaterServlet來分發(fā)請求給不同的Controller,其實這一步是在Servlet的service()方法中執(zhí)行的
二、何時使用攔截器?何時使用過濾器?何時使用監(jiān)聽器
- 具體使用場景
靈活性上說攔截器功能更強大些,Filter能做的事情,他都能做,而且可以在請求前,請求后執(zhí)行,比較靈活。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的優(yōu)先級,值越小優(yōu)先級越高,因此運行順序為
filter3—doFilter—>filter2—doFilter—>filter1—doFilter—>控制器方法—>返回到filter1—>返回到filter2—>返回到filter3注意:@Order或者接口Ordered的作用是定義Spring IOC容器中Bean的執(zhí)行順序的優(yōu)先級,而不是定義Bean的加載順序,Bean的加載順序不受@Order或Ordered接口的影響。
可以看到只有執(zhí)行的時候,才會按filter1,filter2,filter3的順序執(zhí)行。同時我們注意到:先聲明的攔截器 preHandle() 方法先執(zhí)行,而postHandle()方法反而會后執(zhí)行。也即是:postHandle() 方法被調用的順序跟 preHandle() 居然是相反的。如果實際開發(fā)中嚴格要求執(zhí)行順序,那就需要特別注意這一點。那為什么會這樣呢? 得到答案就只能看源碼了,我們要知道controller 中所有的請求都要經過核心組件DispatcherServlet路由,都會執(zhí)行它的 doDispatch() 方法,而攔截器postHandle()、preHandle()方法便是在其中調用的。
- doDispatch方法
看看兩個方法applyPreHandle()、applyPostHandle()具體是如何被調用的,就明白為什么postHandle()、preHandle() 執(zhí)行順序是相反的了。
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);}}}發(fā)現兩個方法中在調用攔截器數組 HandlerInterceptor[] 時,循環(huán)的順序竟然是相反的。。。,導致postHandle()、preHandle() 方法執(zhí)行的順序相反。
五、監(jiān)聽器
Java Web開發(fā)中的監(jiān)聽器(listener)就是由application、session、request三個對象創(chuàng)建、銷毀或者往其中添加、修改、刪除屬性時自動執(zhí)行代碼的功能組件,如下所示:
- ServletContextListener:對Servlet上下文的創(chuàng)建和銷毀進行監(jiān)聽。
- ServletContextAttributeListener:監(jiān)聽Servlet上下文屬性的添加、刪除和修改。
- HttpSessionListener:對Session的創(chuàng)建和銷毀進行監(jiān)聽。
- HttpSessionAttributeListener:對Session對象中屬性的添加、刪除和修改進行監(jiān)聽。
- HttpSessionBindingListener:監(jiān)聽Http會話中對象的綁定信息。
- HttpSessionActivationListener:監(jiān)聽器監(jiān)聽Http會話的情況。
- ServletRequestListener:對請求對象的初始化和銷毀進行監(jiān)聽。
- ServletRequestAttributeListener:對請求對象屬性的添加、刪除和修改進行監(jiān)聽。
- 新建一個servlet處理
- 場景
很多時候當我們完成某些業(yè)務后需要給用戶推送相關消息提醒。對于這種非核心業(yè)務功能我們可以拿出來,創(chuàng)建一個事件去異步執(zhí)行,從而實現核心業(yè)務和子業(yè)務的解耦。
- 實現
定義事件類 Event
創(chuàng)建一個類,繼承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;} }創(chuàng)建一個用于監(jiān)聽指定事件的類,需要實現ApplicationListener接口,說明它是一個應用程序事件的監(jiān)聽類。注意這里需要加上@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());} }- 事件發(fā)布
事件發(fā)布很簡單,只需要使用Spring 提供的ApplicationEventPublisher來發(fā)布自定義事件
- 測試
- 異步執(zhí)行
默認是沒有開啟異步的,我們需要手動配置開啟異步功能,很簡單,只需要在配置類上加上@EnableAsync注解就行了,該注解用于聲明啟用Spring的異步方法執(zhí)行功能,需要和@Configuration注解一起使用,我們可以直接加在啟動類上。然后在監(jiān)聽方法上加上@Async注解,說明當前方法使用異步去執(zhí)行。
可以發(fā)現已經實現了異步功能,主線程為nio-8080-exec-1,監(jiān)聽線程為 task-1。從瀏覽器反應可以看出,接口直接返回了,并沒有等監(jiān)聽線程執(zhí)行完后才返回。
- 自定義異步線程池
用默認的Spring線程池會有啥問題呢?SimpleAsyncTaskExecutor不是真的線程池,這個類不重用線程,每次調用都會創(chuàng)建一個新的線程。很有可能導致OOM。創(chuàng)建配置類(加上@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);}}} }六、 過濾器、攔截器、監(jiān)聽器對比
參考文章
參考文章
參考文章
參考文章
參考文章
總結
以上是生活随笔為你收集整理的过滤器、拦截器、监听器的区别与使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 移动互联网派生app研究报告
- 下一篇: 为什么需要建设中台?