前言:前面2篇部落格,我們分析了Java中過濾器和監聽器的實作原理,今天我們來看看攔截器。
1,攔截器的概念
java裡的攔截器是動态攔截Action調用的對象,它提供了一種機制可以使開發者在一個Action執行的前後執行一段代碼,也可以在一個Action
執行前阻止其執行,同時也提供了一種可以提取Action中可重用部分代碼的方式。在AOP中,攔截器用于在某個方法或者字段被通路之前,進行攔截
然後再之前或者之後加入某些操作。目前,我們需要掌握的主要是Spring的攔截器,Struts2的攔截器不用深究,知道即可。
2,攔截器的原理
大部分時候,攔截器方法都是通過代理的方式來調用的。Struts2的攔截器實作相對簡單。當請求到達Struts2的ServletDispatcher時,Struts2
會查找配置檔案,并根據配置執行個體化相對的攔截器對象,然後串成一個清單(List),最後一個一個的調用清單中的攔截器。Struts2的攔截器是可
插拔的,攔截器是AOP的一個實作。Struts2攔截器棧就是将攔截器按一定的順序連接配接成一條鍊。在通路被攔截的方法或者字段時,Struts2攔截器鍊
中的攔截器就會按照之前定義的順序進行調用。
3,自定義攔截器的步驟
第一步:自定義一個實作了Interceptor接口的類,或者繼承抽象類AbstractInterceptor。
第二步:在配置檔案中注冊定義的攔截器。
第三步:在需要使用Action中引用上述定義的攔截器,為了友善也可以将攔截器定義為預設的攔截器,這樣在不加特殊說明的情況下,所有的
Action都被這個攔截器攔截。
4,過濾器與攔截器的差別
過濾器可以簡單的了解為“取你所想取”,過濾器關注的是web請求;攔截器可以簡單的了解為“拒你所想拒”,攔截器關注的是方法調用,比如攔截
敏感詞彙。
4.1,攔截器是基于java反射機制來實作的,而過濾器是基于函數回調來實作的。(有人說,攔截器是基于動态代理來實作的)
4.2,攔截器不依賴servlet容器,過濾器依賴于servlet容器。
4.3,攔截器隻對Action起作用,過濾器可以對所有請求起作用。
4.4,攔截器可以通路Action上下文和值棧中的對象,過濾器不能。
4.5,在Action的生命周期中,攔截器可以多次調用,而過濾器隻能在容器初始化時調用一次。
5,Spring攔截器
5.1,抽象類HandlerInterceptorAdapter
我們如果在項目中使用了Spring架構,那麼,我們可以直接繼承HandlerInterceptorAdapter.java這個抽象類,來實作我們自己的攔截器。
Spring架構,對java的攔截器概念進行了包裝,這一點和Struts2很類似。HandlerInterceptorAdapter繼承了抽象接口HandlerInterceptor。
[java]
view plain copy- package org.springframework.web.servlet.handler;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.web.servlet.HandlerInterceptor;
- import org.springframework.web.servlet.ModelAndView;
- public abstract class HandlerInterceptorAdapter implements HandlerInterceptor{
- // 在業務處理器處理請求之前被調用
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
- return true;
- }
- // 在業務處理器處理請求完成之後,生成視圖之前執行
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
- throws Exception{
- // 在DispatcherServlet完全處理完請求之後被調用,可用于清理資源
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
- }
接下來我們看一下Spring架構實作的一個簡單的攔截器UserRoleAuthorizationInterceptor,UserRoleAuthorizationInterceptor繼承了
抽象類HandlerInterceptorAdapter,實作了使用者登入認證的攔截功能,如果目前使用者沒有通過認證,會報403錯誤。
- import java.io.IOException;
- import javax.servlet.ServletException;
- public class UserRoleAuthorizationInterceptor extends HandlerInterceptorAdapter{
- // 字元串數組,用來存放使用者角色資訊
- private String[] authorizedRoles;
- public final void setAuthorizedRoles(String[] authorizedRoles){
- this.authorizedRoles = authorizedRoles;
- public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws ServletException, IOException{
- if (this.authorizedRoles != null) {
- for (int i = 0; i < this.authorizedRoles.length; ++i) {
- if (request.isUserInRole(this.authorizedRoles[i])) {
- return true;
- }
- }
- }
- handleNotAuthorized(request, response, handler);
- return false;
- protected void handleNotAuthorized(HttpServletRequest request, HttpServletResponse response, Object handler)
- // 403表示資源不可用。伺服器了解使用者的請求,但是拒絕處理它,通常是由于權限的問題
- response.sendError(403);
下面,我們利用Spring架構提供的HandlerInterceptorAdapter抽過類,來實作一個自定義的攔截器。我們這個攔截器叫做
UserLoginInterceptorBySpring,進行登入攔截控制。工作流程是這樣的:如果目前使用者沒有登入,則跳轉到登入頁面;登入成功後,跳轉到
之前通路的URL頁面。
- import java.util.HashMap;
- import java.util.Map;
- import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
- /**
- * @description 利用spring架構提供的HandlerInterceptorAdapter,實作自定義攔截器
- */
- public class UserLoginInterceptorBySpring extends HandlerInterceptorAdapter{
- // equalsIgnoreCase 與 equals的差別?
- if("GET".equalsIgnoreCase(request.getMethod())){
- //RequestUtil.saveRequest();
- System.out.println("preHandle...");
- String requestUri = request.getRequestURI();
- String contextPath = request.getContextPath();
- String url = requestUri.substring(contextPath.length());
- System.out.println("requestUri" + requestUri);
- System.out.println("contextPath" + contextPath);
- System.out.println("url" + url);
- String username = (String) request.getSession().getAttribute("username");
- if(null == username){
- // 跳轉到登入頁面
- request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
- return false;
- else{
- return true;
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception{
- System.out.println("postHandle...");
- if(modelAndView != null){
- Map<String, String> map = new HashMap<String, String>();
- modelAndView.addAllObjects(map);
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception{
- System.out.println("afterCompletion...");
攔截器是依賴Java反射機制來實作的。攔截器的實作,用到的是JDK實作的動态代理,我們都知道,JDK實作的動态代理,需要依賴接口。攔截器
是在面向切面程式設計中應用的,就是在你的service或者一個方法前調用一個方法,或者在方法後調用一個方法。攔截器不是在web.xml,比如struts在
struts.xml中配置。
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Object result = null;
- System.out.println("方法調用前,可以執行一段代碼" + method.getName());
- result = method.invoke(this.targetObj, args);
- System.out.println("方法調用後,可以執行一段代碼 " + method.getName());
- return result;
- }
總結:
1.過濾器(Filter):所謂過濾器顧名思義是用來過濾的,Java的過濾器能夠為我們提供系統級别的過濾,也就是說,能過濾所有的web請求,
這一點,是攔截器無法做到的。在Java Web中,你傳入的request,response提前過濾掉一些資訊,或者提前設定一些參數,然後再傳入servlet或
者struts的action進行業務邏輯,比如過濾掉非法url(不是login.do的位址請求,如果使用者沒有登陸都過濾掉),或者在傳入servlet或者struts
的action前統一設定字元集,或者去除掉一些非法字元(聊天室經常用到的,一些罵人的話)。filter 流程是線性的,url傳來之後,檢查之後,
可保持原來的流程繼續向下執行,被下一個filter, servlet接收。
2.監聽器(Listener):Java的監聽器,也是系統級别的監聽。監聽器随web應用的啟動而啟動。Java的監聽器在c/s模式裡面經常用到,它
會對特定的事件産生産生一個處理。監聽在很多模式下用到,比如說觀察者模式,就是一個使用監聽器來實作的,在比如統計網站的線上人數。
又比如struts2可以用監聽來啟動。Servlet監聽器用于監聽一些重要事件的發生,監聽器對象可以在事情發生前、發生後可以做一些必要的處理。
3.攔截器(Interceptor):java裡的攔截器提供的是非系統級别的攔截,也就是說,就覆寫面來說,攔截器不如過濾器強大,但是更有針對性。
Java中的攔截器是基于Java反射機制實作的,更準确的劃分,應該是基于JDK實作的動态代理。它依賴于具體的接口,在運作期間動态生成位元組碼。
攔截器是動态攔截Action調用的對象,它提供了一種機制可以使開發者在一個Action執行的前後執行一段代碼,也可以在一個Action執行前阻止其
執行,同時也提供了一種可以提取Action中可重用部分代碼的方式。在AOP中,攔截器用于在某個方法或者字段被通路之前,進行攔截然後再之前或
者之後加入某些操作。java的攔截器主要是用在插件上,擴充件上比如 Hibernate Spring Struts2等,有點類似面向切片的技術,在用之前先要在
配置檔案即xml,檔案裡聲明一段的那個東西。