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這個抽象類,來實現咱們本身的攔截器。java
Spring框架,對java的攔截器概念進行了包裝,這一點和Struts2很相似。HandlerInterceptorAdapter繼承了抽象接口HandlerInterceptor。web
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) throws Exception{ } }
接下來咱們看一下Spring框架實現的一個簡單的攔截器UserRoleAuthorizationInterceptor,UserRoleAuthorizationInterceptor繼承了
抽象類HandlerInterceptorAdapter,實現了用戶登陸認證的攔截功能,若是當前用戶沒有經過認證,會報403錯誤。spring
package org.springframework.web.servlet.handler; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; 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) throws ServletException, IOException{ // 403表示資源不可用。服務器理解用戶的請求,可是拒絕處理它,一般是因爲權限的問題 response.sendError(403); } }
下面,咱們利用Spring框架提供的HandlerInterceptorAdapter抽過類,來實現一個自定義的攔截器。咱們這個攔截器叫作
UserLoginInterceptorBySpring,進行登陸攔截控制。工做流程是這樣的:若是當前用戶沒有登陸,則跳轉到登陸頁面;登陸成功後,跳轉到
以前訪問的URL頁面。編程
import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; /** * @description 利用spring框架提供的HandlerInterceptorAdapter,實現自定義攔截器 */ public class UserLoginInterceptorBySpring extends HandlerInterceptorAdapter{ // 在業務處理器處理請求以前被調用 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{ // 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); } } // 在DispatcherServlet徹底處理完請求以後被調用,可用於清理資源 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; }