Like Share Discussion Bookmark Smile

J.J. Huang   2019-04-27   Spring Boot   瀏覽次數:

SpringBoot - 第三十八章 | 過濾器,監聽器,攔截器

在開發過程中,總存在著這樣的場景,比如攔截請求的ip地址,或者在所有的請求都返回相同的資料,如果每一個方法都寫出相同資料固然可以實現,但是隨著項目的變大,重複的代碼會越來越多,所以在這種情況我們可以用過濾器、監聽器、攔截器來實現以上功能。


過濾器(Filter)

過濾器Filter,是Servlet的的一個實用技術了。可通過過濾器,對請求進行攔截,比如讀取session判斷用戶是否登錄、判斷訪問的請求URL是否有訪問權限(黑白名單)等。主要還是可對請求進行預處理。接下來介紹下,在springboot如何實現過濾器功能。

@WebFilter

@WebFilter時Servlet3.0新增的註解,原先實現過濾器,需要在web.xml中進行配置,而現在通過此註解,啟動啟動時會自動掃描自動註冊。

建立 CustomFilter

修改 Chapter38Application

加入@ServletComponentScan註解

測試結果

1
2
3
4
5
6
7
8
9
10
11
2019-04-27 19:41:43.526  INFO 683 --- [           main] c.j.l.s.chapter38.Chapter38Application   : Starting Chapter38Application on localhost with PID 683 (/Users/morose/Documents/workspace-SpringBoot/chapter-38/target/classes started by morose in /Users/morose/Documents/workspace-SpringBoot/chapter-38)
2019-04-27 19:41:43.530 INFO 683 --- [ main] c.j.l.s.chapter38.Chapter38Application : No active profile set, falling back to default profiles: default
2019-04-27 19:41:44.589 INFO 683 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2019-04-27 19:41:44.619 INFO 683 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-04-27 19:41:44.619 INFO 683 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-04-27 19:41:44.732 INFO 683 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-04-27 19:41:44.732 INFO 683 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1142 ms
2019-04-27 19:41:44.801 INFO 683 --- [ main] c.j.l.s.chapter38.config.CustomFilter : filter 初始化
2019-04-27 19:41:45.035 INFO 683 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-04-27 19:41:45.266 INFO 683 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-04-27 19:41:45.270 INFO 683 --- [ main] c.j.l.s.chapter38.Chapter38Application : Started Chapter38Application in 2.072 seconds (JVM running for 2.443)

過濾器已經生效了。但當註冊多個過濾器時,無法指定執行順序的,原本使用web。 xml配置過濾器時,是可指定執行順序的,但使用@WebFilter時,沒有這個配置屬性的(需要配合@Order進行),所以接下來介紹下通過FilterRegistrationBean進行過濾器的註冊。
通過過濾器的java類名稱,進行順序的約定,比如LogFilter和AuthFilter,此時AuthFilter就會比LogFilter先執行,因為首字母A比L前面。《關於@webFilter使用@Order無效問題》

FilterRegistrationBean方式

FilterRegistrationBean是springboot提供的,此類提供setOrder方法,可以為filter設置排序值,讓spring在註冊web filter之前排序後再依次註冊。

修改 CustomFilter

註解掉@WebFilter

1
\\ @WebFilter(filterName = "customFilter", urlPatterns = {"/*"})

修改 Chapter38Application

啟動類中利用@bean註冊FilterRegistrationBean

測試結果

1
2
3
4
5
6
7
8
9
10
11
2019-04-27 20:12:49.425  INFO 772 --- [           main] c.j.l.s.chapter38.Chapter38Application   : Starting Chapter38Application on localhost with PID 772 (/Users/morose/Documents/workspace-SpringBoot/chapter-38/target/classes started by morose in /Users/morose/Documents/workspace-SpringBoot/chapter-38)
2019-04-27 20:12:49.428 INFO 772 --- [ main] c.j.l.s.chapter38.Chapter38Application : No active profile set, falling back to default profiles: default
2019-04-27 20:12:50.615 INFO 772 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2019-04-27 20:12:50.646 INFO 772 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-04-27 20:12:50.646 INFO 772 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-04-27 20:12:50.754 INFO 772 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-04-27 20:12:50.754 INFO 772 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1251 ms
2019-04-27 20:12:50.816 INFO 772 --- [ main] c.j.l.s.chapter38.config.CustomFilter : filter 初始化
2019-04-27 20:12:51.040 INFO 772 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-04-27 20:12:51.289 INFO 772 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-04-27 20:12:51.293 INFO 772 --- [ main] c.j.l.s.chapter38.Chapter38Application : Started Chapter38Application in 2.293 seconds (JVM running for 2.78)

監聽器(Listeeshi)

Listeeshi是servlet規範中定義的一種特殊類。用於監聽servletContext、HttpSession和servletRequest等域對象的建立和銷毀事件。監聽域對象的屬性發生修改的事件。用於在事件發生前、發生後做一些必要的處理。一般是獲取在線人數等業務需求。

建立 Customlister

修改 Chapter38Application

加入@ServletComponentScan註解,進行自動註冊即可。

測試結果

啟動應用,http://localhost:8080

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2019-04-27 20:20:46.638  INFO 784 --- [           main] c.j.l.s.chapter38.Chapter38Application   : Starting Chapter38Application on localhost with PID 784 (/Users/morose/Documents/workspace-SpringBoot/chapter-38/target/classes started by morose in /Users/morose/Documents/workspace-SpringBoot/chapter-38)
2019-04-27 20:20:46.641 INFO 784 --- [ main] c.j.l.s.chapter38.Chapter38Application : No active profile set, falling back to default profiles: default
2019-04-27 20:20:47.837 INFO 784 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2019-04-27 20:20:47.870 INFO 784 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-04-27 20:20:47.870 INFO 784 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-04-27 20:20:47.986 INFO 784 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-04-27 20:20:47.986 INFO 784 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1254 ms
2019-04-27 20:20:48.056 INFO 784 --- [ main] c.j.l.s.chapter38.config.CustomFilter : filter 初始化
2019-04-27 20:20:48.287 INFO 784 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-04-27 20:20:48.531 INFO 784 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-04-27 20:20:48.535 INFO 784 --- [ main] c.j.l.s.chapter38.Chapter38Application : Started Chapter38Application in 2.316 seconds (JVM running for 2.742)
2019-04-27 20:28:18.445 INFO 784 --- [nio-8080-exec-2] c.j.l.s.chapter38.config.Customlister : 監聽器:初始化
2019-04-27 20:28:18.445 INFO 784 --- [nio-8080-exec-1] c.j.l.s.chapter38.config.Customlister : 監聽器:初始化
2019-04-27 20:28:18.450 INFO 784 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-04-27 20:28:18.450 INFO 784 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-04-27 20:28:18.455 INFO 784 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
2019-04-27 20:28:18.463 INFO 784 --- [nio-8080-exec-1] c.j.l.s.chapter38.config.CustomFilter : doFilter 請求處理
2019-04-27 20:28:18.463 INFO 784 --- [nio-8080-exec-2] c.j.l.s.chapter38.config.CustomFilter : doFilter 請求處理
2019-04-27 20:28:18.506 INFO 784 --- [nio-8080-exec-2] c.j.l.s.chapter38.config.Customlister : 監聽器:銷毀
2019-04-27 20:28:18.561 INFO 784 --- [nio-8080-exec-1] c.j.l.s.chapter38.config.Customlister : 監聽器:銷毀
2019-04-27 20:28:18.743 INFO 784 --- [nio-8080-exec-3] c.j.l.s.chapter38.config.Customlister : 監聽器:初始化
2019-04-27 20:28:18.744 INFO 784 --- [nio-8080-exec-3] c.j.l.s.chapter38.config.CustomFilter : doFilter 請求處理
2019-04-27 20:28:18.764 INFO 784 --- [nio-8080-exec-3] c.j.l.s.chapter38.config.Customlister : 監聽器:銷毀

攔截器(Interceptor)

以上的過濾器、監聽器都屬於Servlet的api,我們在開發中處理利用以上的進行過濾web請求時,還可以使用Spring提供的攔截器(HandlerInterceptor)進行更加精細的控制。

建立 CustomHandlerInterceptor

建立 WebMvcConfig

(注意spring boot 1.x 和 2.x使用方式略有不同)

測試結果

啟動應用,http://localhost:8080

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2019-04-27 20:54:57.318  INFO 895 --- [           main] c.j.l.s.chapter38.Chapter38Application   : Starting Chapter38Application on localhost with PID 895 (/Users/morose/Documents/workspace-SpringBoot/chapter-38/target/classes started by morose in /Users/morose/Documents/workspace-SpringBoot/chapter-38)
2019-04-27 20:54:57.323 INFO 895 --- [ main] c.j.l.s.chapter38.Chapter38Application : No active profile set, falling back to default profiles: default
2019-04-27 20:54:58.395 INFO 895 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2019-04-27 20:54:58.427 INFO 895 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-04-27 20:54:58.427 INFO 895 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-04-27 20:54:58.542 INFO 895 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-04-27 20:54:58.542 INFO 895 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1154 ms
2019-04-27 20:54:58.614 INFO 895 --- [ main] c.j.l.s.chapter38.config.CustomFilter : filter 初始化
2019-04-27 20:54:58.858 INFO 895 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-04-27 20:54:59.115 INFO 895 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-04-27 20:54:59.118 INFO 895 --- [ main] c.j.l.s.chapter38.Chapter38Application : Started Chapter38Application in 2.092 seconds (JVM running for 2.457)
2019-04-27 20:55:01.684 INFO 895 --- [nio-8080-exec-1] c.j.l.s.chapter38.config.Customlister : 監聽器:初始化
2019-04-27 20:55:01.690 INFO 895 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-04-27 20:55:01.691 INFO 895 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-04-27 20:55:01.698 INFO 895 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 7 ms
2019-04-27 20:55:01.709 INFO 895 --- [nio-8080-exec-1] c.j.l.s.chapter38.config.CustomFilter : doFilter 請求處理
2019-04-27 20:55:01.729 INFO 895 --- [nio-8080-exec-1] c.j.l.s.c.c.CustomHandlerInterceptor : 開始攔截登錄請求....
2019-04-27 20:55:01.732 INFO 895 --- [nio-8080-exec-1] c.j.l.s.chapter38.config.Customlister : 監聽器:銷毀

請求鏈路說明

攔截器與過濾器的區別

攔截器是AOP( Aspect-Oriented Programming)的一種實現,底層通過動態代理模式完成。

(1)攔截器是基於java的反射機制的,而過濾器是基於函數回調。
(2)攔截器不依賴於servlet容器,而過濾器依賴於servlet容器。
(3)攔截器只能對Controller請求起作用,而過濾器則可以對幾乎所有的請求起作用。
(4)在Controller的生命週期中,攔截器可以多次被調用,而過濾器只能在容器初始化時被調用一次。

過濾器應用場景:設置編碼字符、過濾銘感字符
攔截器應用場景:攔截未登陸用戶、審計日誌

註:以上參考了
servlet/filter/listener/interceptor区别与联系
(十九)SpringBoot2.0整合拦截器
SpringBoot拦截器
oKongSpringBoot | 第七章:过滤器、监听器、拦截器 文章。