目錄
介紹一下
1、過濾器和攔截器(只針對Servlet的Filter接口和SpringMVC的HandlerInterceptor接口,不針對其它實現方式,手擼服務器的大神再見)
過濾器和攔截器都是將客戶端的請求進行攔截處理,然後將請求轉交給下一資源。其處理方式如下圖所示。
攔截器和過濾都能實現相同的功能,那麼具體差別在哪裏呢?
(現在網上很多都是相互摘抄,錯誤一大堆,感覺沒有營養,所以談談自己的使用看法,不談實現)
a、過濾器依賴web容器實現,攔截器依賴mvc框架實現,也就導致其作用順序是不同的,另外就是過濾器的作用域應該是包裹攔截器的(即過濾器在攔截器之前執行(親自驗證))。另外Filter接口提供了init()方法和destroy()方法,相對來說想在web容器初始化和銷燬的時候實現一些業務邏輯,過濾器相對簡單一些。
它們之間體現的一個包裹關係如下圖所示(圖是複製的,謝謝原圖作者)。
b、在現在Spring大行其道的情況下,過濾器和攔截器都可以交給IoC管理,從而使用依賴注入(很多文章說不行,是錯誤的,但是在過濾器的init()和destroy()方法中避免使用依賴注入,因爲IoC的生命週期和web容器的生命週期是不一樣的,總之,避免在IoC沒有初始化之前去使用IoC)。在SpringMVC中封裝的攔截器靈活性更強,使用更加容易,優先考慮攔截器。
注意:
Filter接口中使用的是javax.servlet.ServletRequest和javax.servlet.ServletResponse,HandlerInterceptor接口中使用的是javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse。
HttpServletRequest繼承自ServletRequest,HttpServletResponse繼承自ServletResponse。當然HttpServletRequest和HttpServletResponse功能更加強大。
在Filter中是可以將ServletRequest和ServletResponse強轉成HttpServletRequest和HttpServletResponse的。
都知道,向上轉型是絕對安全的,而向下轉型不一定安全。這裏能這麼做,是因爲它們都是接口,要獲取一個接口的實例,只能實例化它們的實現類。在tomcat中,只有一個實現類,分別是由org.apache.catalina.connector.RequestFacade和org.apache.catalina.connector.ResponseFacade去實現。
2、監聽器(這裏主指Servlet監聽器)
在Servlet中已經定義好了一些事件,監聽器主要作用於監聽Servlet的有效事件。作用是監聽由於Web應用中狀態改變而引起的Servlet容器產生的相應事件,然後接受並處理這些事件。
過濾器實現
爲了方便,在SpringBoot項目中實現。
1、實現Filter接口
//@Order(value=1) 用於多個Filter指定過濾器順序
@WebFilter(filterName="myFilter1", urlPatterns= {"/*"})//標記過濾器,攔截所有請求
public class MyFilter1 implements Filter {
/**
* 過濾器初始化方法,該方法在過濾器初始化時調用(即容器加載時)
* @param filterConfig FilterConfig也是接口對象 由Servlet容器實現,主要
* 用於獲取過濾器中的配置信息,其常用方法聲明如下:
* String getFilterName 獲取過濾器名字
* ServletContext getServletContext() 獲取Servlet上下文
* String getInitParameter(String name) 獲取過濾器的初始化參數值
* Enumeration getInitParameterNames() 獲取過濾器的所有初始化參數
* */
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//TODO 沒有業務邏輯則不寫相應的實現
}
/**
* 對請求進行過濾處理(也就是主要實現過濾功能的方法)
* @param chain FilterChain接口也是由Servlet容器實現。
* 其只有一個方法,如下:
* void doFilter(ServletRequest request, ServletResponse response)
* 該方法用於將過濾後的請求傳遞給下一個過濾器。如果是最後一個過濾器,則將請求轉交給目標資源。
* @param request ServletRequest可強轉爲HttpServletRequest ,其都由Servlet實現
* @param response ServletResponse可強轉爲HttpServletResponse,因爲其都由Servlet實現
* */
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//TODO 具體的過濾邏輯實現--比如登錄權限驗證、字符編碼過濾等
/*
* 將請求轉交給下一過濾器或其它目標資源
* 注:如果沒有調用此方法,則請求會在此處終止(也不會響應客戶端)。
* 可以直接使用response直接響應客戶端(比如在權限驗證不通過的時候)。
* */
chain.doFilter(request, response);
}
/**
* 過濾器銷燬方法,以便釋放資源(即關閉容器時調用)
* */
@Override
public void destroy() {
//TODO 沒有業務邏輯則不寫相應的實現
}
}
2、在啓動類上加上Servlet註解掃描
@SpringBootApplication
@ServletComponentScan//Servlet註解掃描
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
攔截器實現
爲了方便,在SpringBoot項目中實現。
1、實現HandlerInterceptor接口
/**
* 可將其標記爲Spring的組件
* 或在註冊攔截器的時候利用構造方法傳遞其它Spring的組件(如其它Service層或Dao層)
* */
//@Order(value=1)//用於多個攔截器指定執行順序
public class MyHandlerInterceptor1 implements HandlerInterceptor {
/**
* <p>該方法就是進行攔截處理的,將在Controller方法處理之前調用
* <p>該方法返回True時攔截器纔會繼續往下執行,返回false則會結束整個請求
* <p>可以直接使用response或全局異常攔截直接響應客戶端
* */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO 具體的攔截邏輯 --比如登錄權限校驗、非法敏感字符校驗等
//根據不同的邏輯返回boolean類型即可 也可直接使用response或全局異常攔截直接響應客戶端
return HandlerInterceptor.super.preHandle(request, response, handler);
}
/**
* <p>該方法將在處理請求完成後(即Controller方法調用之後)視圖渲染之前的處理操作
* <p>該方法只能在當前類的preHandle()方法返回true時纔會執行
* */
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO 沒有相關業務邏輯 則不用重寫此方法
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* <p>該方法將在整個請求和相應動作完成之後執行(即視圖渲染之後)
* <p>該方法只能在當前類的preHandle()方法返回true時纔會執行
* */
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO 沒有相關業務邏輯 則不用重寫此方法
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
2、註冊攔截器,實現WebMvcConfigurer接口。
@Configuration
public class InterceptConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//註冊攔截器 可利用構造方法傳遞一些參數
registry.addInterceptor(new MyHandlerInterceptor1())
//攔截所有請求
.addPathPatterns("/**");
//還可排除一些靜態資源請求攔截
//.excludePathPatterns(null);
}
Servlet監聽器實現
1、實現Servlet中定義的監聽器接口,如ServletContextListener(Servlet還定義了其它監聽器)
/**
* <p>Servlet監聽器
* <p>@WebListener註解只有一個屬性 用於描述這個監聽器 可以默認不要
* */
@WebListener(value = "監聽Servlet上下文的創建和刪除")
public class MyListener implements ServletContextListener {
/**
* <p>通知正在收聽的對象,應用程序已經被加載及初始化
* */
@Override
public void contextInitialized(ServletContextEvent sce) {
//TODO 沒有相應的業務邏輯 則不寫實現
}
/**
* <p>通知正在收聽的對象,應用程序已經被銷燬(即關閉)
* */
@Override
public void contextDestroyed(ServletContextEvent sce) {
//TODO 沒有相應的業務邏輯 則不寫實現
}
}
2、在啓動類上加上Servlet註解掃描
@SpringBootApplication
@ServletComponentScan//Servlet註解掃描
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
3、Servlet其它監聽器
Servlet監聽器的功能就是監聽由於Web應用中狀態改變而引起的Servlet容器產生的相應事件,
比如利用HttpSessionBindingListener統計在線人數。
除了上述的ServletContextListener,還有以下監聽器(共8個)。如下表所示:
Servlet上下文監聽 | ServletContextListener | 主要監聽Servlet上下文的創建和刪除 |
ServletContextAttributeListener | 主要監聽Servlet上下文屬性的增加、刪除和修改 | |
HTTP會話監聽 | HttpSessionListener | 主要監聽HTTP會話創建和銷燬 |
HttpSessionActivationListener | 實現監聽HTTP會話active和passivate | |
HttpSessionAttributeListener | 實現監聽HTTP會話中屬性的設置請求 | |
HttpSessionBindingListener | 實現監聽HTTP會話中對象的綁定信息 | |
Servlet請求監聽 | ServletRequestListener | 實現監聽客戶端的請求 |
ServletRequestAttributeListener | 實現講臺客戶端請求屬性變化 |
ServletContextAttributeListener
public class MyListener2 implements ServletContextAttributeListener {
/**
* <p>當有對象加入Application範圍時,通知正在收聽的對象
* */
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
// TODO Auto-generated method stub
}
/**
* <p>當有對象從Application範圍中移除,通知正在收聽的對象
* */
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
// TODO Auto-generated method stub
}
/**
* <p>擋在Application的範圍有對象取代另一個對象時,通知正在收聽的對象
* */
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
// TODO Auto-generated method stub
}
}
HttpSessionListener
public class MyListener2 implements HttpSessionListener {
/**
* <p>通知正在收聽的對象,session已經被加載及初始化
* */
@Override
public void sessionCreated(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
/**
* <p>通知正在收聽的對象,session已經被載出銷燬
* @param se HttpSessionEvent的主要方法是getSession(),可以使用
* 該方法回傳一個session對象
* */
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
}
HttpSessionActivationListener
public class MyListener2 implements HttpSessionActivationListener {
/**
* <p>通知正在收聽的對象,它的session已經被變爲無效狀態
* */
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
/**
* <p>通知正在收聽的對象,它的session已經變爲有效狀態
* */
@Override
public void sessionDidActivate(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
}
HttpSessionAttributeListener
public class MyListener2 implements HttpSessionAttributeListener {
/**
* <p>當有對象加入session範圍時,通知正在收聽的對象
* */
@Override
public void attributeAdded(HttpSessionBindingEvent se) {
// TODO Auto-generated method stub
}
/**
* <p>當有對象從session範圍移除時,通知正在收聽的對象
* @param se HttpSessionBindingEvent類主要有3個方法
* getName()、getSession()、getValues()
* */
@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
// TODO Auto-generated method stub
}
/**
* <p>當在session的範圍有對象取代另一個對象時,通知正在收聽的對象
* */
@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
// TODO Auto-generated method stub
}
}
HttpSessionBindingListener
public class MyListener2 implements HttpSessionBindingListener {
/**
* <p>當有對象加入session範圍時會被自動調用
* */
@Override
public void valueBound(HttpSessionBindingEvent event) {
// TODO Auto-generated method stub
}
/**
* <p>當有對象從session的範圍內移除時會被自動調用
* */
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
// TODO Auto-generated method stub
}
}
ServletRequestListener
public class MyListener2 implements ServletRequestListener {
/**
* <p>通知正在收聽的對象,ServletRequest已經被加載及初始化
* */
@Override
public void requestInitialized(ServletRequestEvent sre) {
// TODO Auto-generated method stub
}
/**
* <p>通知正在收聽的對象,ServletRequest已經被載出銷燬
* */
@Override
public void requestDestroyed(ServletRequestEvent sre) {
// TODO Auto-generated method stub
}
}
ServletRequestAttributeListener
public class MyListener2 implements ServletRequestAttributeListener {
/**
* <p>當有對象加入request範圍時,通知正在收聽的對象
* */
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
// TODO Auto-generated method stub
}
/**
* <p>當有對象從request範圍移除時,通知正在收聽的對象
* */
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
// TODO Auto-generated method stub
}
/**
* <p>當在request範圍內有對象取代另一個對象時,通知正在收聽的對象
* */
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
// TODO Auto-generated method stub
}
}