寫在前面:
使用Spring-Boot時,嵌入式Servlet容器可以通過掃描注解(@ServletComponentScan)的方式注冊Servlet、Filter和Servlet規范的所有監聽器(如HttpSessionListener監聽器)。
Spring boot 的主 Servlet 為 DispatcherServlet,其默認的url-pattern為“/”。一般情況系統默認的Servlet就夠用了,如果需要自定義Servlet,可以繼承系統抽象類HttpServlet,重寫方法來實現自己的Servlet。關於Servlet、過濾器、攔截器、監聽器可以參考:(轉)servlet、filter、listener、interceptor之間的區別和聯系
Spring-Boot有兩種方法注冊Servlet、Filter和Listener :
1、代碼注冊:通過ServletRegistrationBean、 FilterRegistrationBean 和 ServletListenerRegistrationBean 獲得控制。
2、在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通過 @WebServlet、@WebFilter、@WebListener 注解自動注冊,無需其他代碼。
一、Servlet
Servlet匹配規則:匹配的優先級是從精確到模糊,復合條件的Servlet並不會都執行。
1、通過@ServletComponentScan自動掃描
a、springboot的啟動入口添加注解:@ServletComponentScan;
@SpringBootApplication @ServletComponentScan public class ApplicationMain { public static void main(String[] args) { SpringApplication.run(ApplicationMain.class, args); } }
b、@WebServlet 自定義Servlet,配置處理請求路徑 /demo/myServlet
@WebServlet(name = "myServletDemo1",urlPatterns = "/demo/myServlet",description = "自定義的servlet") public class MyServletDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("==========myServletDemo Get Method=========="); resp.getWriter().println("my myServletDemo1 process request"); super.doGet(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("==========myServletDemo1 POST Method=========="); super.doPost(req, resp); } }
2、使用@ServletRegistrationBean注解
a、@ServletRegistrationBean注入自定義的Servlet,配置處理的路徑為 /demo/servletDemo2
@Configuration public class ServletConfiguration { /** * 代碼注入 */ @Bean public ServletRegistrationBean myServletDemo() { return new ServletRegistrationBean(new MyServletDemo2(), "/demo/servletDemo2"); } }
b、自定義的Servlet
public class MyServletDemo2 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("==========myServletDemo2 Get Method=========="); resp.getWriter().println("my myServletDemo2 process request"); super.doGet(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("==========myServletDemo2 POST Method=========="); super.doPost(req, resp); } }
二、Filter
完整的流程是:Filter對用戶請求進行預處理,接着將請求交給Servlet進行處理並生成響應,最后Filter再對服務器響應進行后處理。
Filter有如下幾個用處。
- 在HttpServletRequest到達Servlet之前,攔截客戶的HttpServletRequest。
- 根據需要檢查HttpServletRequest,也可以修改HttpServletRequest頭和數據。
- 在HttpServletResponse到達客戶端之前,攔截HttpServletResponse。
- 根據需要檢查HttpServletResponse,也可以修改HttpServletResponse頭和數據。
多個FIlter可以組成過濾器調用鏈,按設置的順序逐一進行處理,形成Filter調用鏈。
1、通過@ServletComponentScan自動掃描
a、springboot的啟動入口添加注解:@ServletComponentScan;
b、@WebFilter 配置處理全部url的Filter
@WebFilter(filterName = "myFilter",urlPatterns = "/*") public class MyFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { System.out.println(">>>>>>myFilter init ……"); } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println(">>>>>>執行過濾操作"); filterChain.doFilter(servletRequest, servletResponse); } public void destroy() { System.out.println(">>>>>>myFilter destroy ……"); } }
* doFilter()方法是過濾器的核心方法,實現該方法就可實現對用戶請求進行預處理,也可實現對服務器響應進行后處理——它們的分界線為是否調用了filterChain.doFilter(),執行該方法之前,即對用戶請求進行預處理;執行該方法之后,即對服務器響應進行后處理。
2、通過@FilterRegistrationBean注冊
a、@Bean注入自定義的Filter
@Configuration public class ServletConfiguration { @Bean public FilterRegistrationBean myFilterDemo(){ FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new MyFilter2()); registration.addUrlPatterns("/demo/myFilter2"); registration.addInitParameter("paramName", "paramValue"); registration.setName("myFilter2"); registration.setOrder(2);//指定filter的順序 return registration; } }

b、自定義的Filter
public class MyFilter2 implements Filter { public void init(FilterConfig filterConfig) throws ServletException { System.out.println("======MyFilter2 init ……"); } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("======MyFilter2執行過濾操作"); filterChain.doFilter(servletRequest, servletResponse); } public void destroy() { System.out.println("======MyFilter2 destroy ……"); } }
三、Listener
目前 Servlet 中提供了 6 種兩類事件的觀察者接口,它們分別是:4 個 EventListeners 類型的,ServletContextAttributeListener、ServletRequestAttributeListener、ServletRequestListener、HttpSessionAttributeListener 和 2 個 LifecycleListeners 類型的,ServletContextListener、HttpSessionListener。
- ServletContextAttributeListener監聽對ServletContext屬性的操作,比如增加、刪除、修改屬性。
- ServletContextListener監聽ServletContext。當創建ServletContext時,激發contextInitialized(ServletContextEvent sce)方法;當銷毀ServletContext時,激發contextDestroyed(ServletContextEvent sce)方法。
- HttpSessionListener監聽HttpSession的操作。當創建一個Session時,激發session Created(HttpSessionEvent se)方法;當銷毀一個Session時,激發sessionDestroyed (HttpSessionEvent se)方法。
- HttpSessionAttributeListener監聽HttpSession中的屬性的操作。當在Session增加一個屬性時,激發attributeAdded(HttpSessionBindingEvent se) 方法;當在Session刪除一個屬性時,激發attributeRemoved(HttpSessionBindingEvent se)方法;當在Session屬性被重新設置時,激發attributeReplaced(HttpSessionBindingEvent se) 方法。
1、通過@ServletComponentScan自動掃描
a、ServletListenerRegistrationBean 注入自定義的Listener;
b、自定義的Listener
@WebListener public class MyLisener implements ServletContextListener { public void contextInitialized(ServletContextEvent servletContextEvent) { System.out.println("MyLisener contextInitialized method"); } public void contextDestroyed(ServletContextEvent servletContextEvent) { System.out.println("MyLisener contextDestroyed method"); } }
2、通過@ServletListenerRegistrationBean 注冊
a、@Bean注入自定義的Listener;
@Configuration public class ServletConfiguration { @Bean public ServletListenerRegistrationBean myListener(){ return new ServletListenerRegistrationBean(new MyLisener()); } }
b、自定義的Listener
public class MyLisener implements ServletContextListener { public void contextInitialized(ServletContextEvent servletContextEvent) { System.out.println("MyLisener contextInitialized method"); } public void contextDestroyed(ServletContextEvent servletContextEvent) { System.out.println("MyLisener contextDestroyed method"); } }
四、驗證servlet、filter、listener的順序
a、使用MyServletDemo2、MyFilter2、MyFilter3、MyLisener做測試;
b、設置servlet處理url格式為 /demo/*;設置MyFilter2順序為2;MyFilter3的順序為3;
@Configuration public class ServletConfiguration { /**代碼注入*/ @Bean public ServletRegistrationBean myServletDemo(){ return new ServletRegistrationBean(new MyServletDemo2(),"/demo/*"); } @Bean public FilterRegistrationBean myFilterDemo(){ FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new MyFilter2()); registration.addUrlPatterns("/demo/myFilter"); registration.addInitParameter("paramName", "paramValue"); registration.setName("myFilter2"); registration.setOrder(2);//指定filter的順序 return registration; } @Bean public FilterRegistrationBean myFilterDemo2(){ FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new MyFilter3()); registration.addUrlPatterns("/demo/*"); registration.addInitParameter("paramName", "paramValue"); registration.setName("myFilter3"); registration.setOrder(1); return registration; } @Bean public ServletListenerRegistrationBean myListener(){ return new ServletListenerRegistrationBean(new MyLisener()); } }
c、啟動項目后輸出:(FIlter2先執行init,因為@Ben在前)
MyLisener contextInitialized method
======MyFilter2 init ……
======MyFilter3 init ……
d、瀏覽器輸入地址地址:http://localhost:8080/demo/myFilter,輸出:
======MyFilter3執行過濾操作
======MyFilter2執行過濾操作
>>>>>>>>>>test Get Method==========
可以看出:
- Filter3比Filter2先執行;
- Filter可以匹配上的url都會執行,並且按順序執行(Filter的調用鏈);
- Filter比servlet先執行。
- servlet先按具體匹配,然后模糊匹配,並且只能有一個servlet匹配上,沒有servlet調用鏈。
執行順序是:Listener》Filter》Servlet
五、ApplicationListener自定義偵聽器類
參考:http://blog.csdn.net/liaokailin/article/details/48186331
六、Interceptor
攔截器只會處理DispatcherServlet處理的url
a、自定義攔截器
public class MyInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { System.out.println(">>>>MyInterceptor preHandle"); return true; } public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { System.out.println(">>>>MyInterceptor postHandle"); } public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { System.out.println(">>>>MyInterceptor afterCompletion"); } }
b、注冊攔截器
@Configuration public class WebMvcConfiguration extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**"); super.addInterceptors(registry); } }
c、攔截器驗證
輸入地址:http://localhost:8080/home/test
>>>>MyInterceptor preHandle
>>>>MyInterceptor postHandle
>>>>MyInterceptor afterCompletion
輸入地址:http://localhost:8080/demo/myFilter (自定義的Servlet處理了請求,此時攔截器不處理)
攔截器不處理。
