Java攔截器Interceptor實現(xiàn)原理及代碼示例
?
1,攔截器的概念
java里的攔截器是動態(tài)攔截Action調(diào)用的對象,它提供了一種機制可以使開發(fā)者在一個Action執(zhí)行的前后執(zhí)行一段代碼,也可以在一個Action執(zhí)行前阻止其執(zhí)行,同時也提供了一種可以提取Action中可重用部分代碼的方式。在AOP中,攔截器用于在某個方法或者字段被訪問之前,進行攔截然后再之前或者之后加入某些操作。目前,我們需要掌握的主要是Spring的攔截器,Struts2的攔截器不用深究,知道即可。
2,攔截器的原理
大部分時候,攔截器方法都是通過代理的方式來調(diào)用的。Struts2的攔截器實現(xiàn)相對簡單。當(dāng)請求到達Struts2的ServletDispatcher時,Struts2會查找配置文件,并根據(jù)配置實例化相對的攔截器對象,然后串成一個列表(List),最后一個一個的調(diào)用列表中的攔截器。Struts2的攔截器是可插拔的,攔截器是AOP的一個實現(xiàn)。Struts2攔截器棧就是將攔截器按一定的順序連接成一條鏈。在訪問被攔截的方法或者字段時,Struts2攔截器鏈中的攔截器就會按照之前定義的順序進行調(diào)用。
3,自定義攔截器的步驟
- 第一步:自定義一個實現(xiàn)了Interceptor接口的類,或者繼承抽象類AbstractInterceptor。
- 第二步:在配置文件中注冊定義的攔截器。
- 第三步:在需要使用Action中引用上述定義的攔截器,為了方便也可以將攔截器定義為默認的攔截器,這樣在不加特殊說明的情況下,所有的Action都被這個攔截器攔截。
4,過濾器與攔截器的區(qū)別
過濾器可以簡單的理解為“取你所想取”,過濾器關(guān)注的是web請求;攔截器可以簡單的理解為“拒你所想拒”,攔截器關(guān)注的是方法調(diào)用,比如攔截敏感詞匯。
- 4.1,攔截器是基于java反射機制來實現(xiàn)的,而過濾器是基于函數(shù)回調(diào)來實現(xiàn)的。(有人說,攔截器是基于動態(tài)代理來實現(xiàn)的)
- 4.2,攔截器不依賴servlet容器,過濾器依賴于servlet容器。
- 4.3,攔截器只對Action起作用,過濾器可以對所有請求起作用。
- 4.4,攔截器可以訪問Action上下文和值棧中的對象,過濾器不能。
- 4.5,在Action的生命周期中,攔截器可以多次調(diào)用,而過濾器只能在容器初始化時調(diào)用一次。
5,Spring攔截器
抽象類HandlerInterceptorAdapter
我們?nèi)绻陧椖恐惺褂昧薙pring框架,那么,我們可以直接繼承HandlerInterceptorAdapter.java
這個抽象類,來實現(xiàn)我們自己的攔截器。
Spring框架,對java的攔截器概念進行了包裝,這一點和Struts2很類似。HandlerInterceptorAdapter繼承了抽象接口HandlerInterceptor。
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{ // 在業(yè)務(wù)處理器處理請求之前被調(diào)用 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{ return true; } // 在業(yè)務(wù)處理器處理請求完成之后,生成視圖之前執(zhí)行 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception{ } // 在DispatcherServlet完全處理完請求之后被調(diào)用,可用于清理資源 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception{ } }
接下來我們看一下Spring框架實現(xiàn)的一個簡單的攔截器UserRoleAuthorizationInterceptor,UserRoleAuthorizationInterceptor繼承了抽象類HandlerInterceptorAdapter,實現(xiàn)了用戶登錄認證的攔截功能,如果當(dāng)前用戶沒有通過認證,會報403錯誤。
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{ // 字符串?dāng)?shù)組,用來存放用戶角色信息 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表示資源不可用。服務(wù)器理解用戶的請求,但是拒絕處理它,通常是由于權(quán)限的問題 response.sendError(403); } }
下面,我們利用Spring框架提供的HandlerInterceptorAdapter抽過類,來實現(xiàn)一個自定義的攔截器。我們這個攔截器叫做UserLoginInterceptorBySpring,進行登錄攔截控制。
工作流程是這樣的:如果當(dāng)前用戶沒有登錄,則跳轉(zhuǎn)到登錄頁面;登錄成功后,跳轉(zhuǎn)到之前訪問的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,實現(xiàn)自定義攔截器 */ public class UserLoginInterceptorBySpring extends HandlerInterceptorAdapter{ // 在業(yè)務(wù)處理器處理請求之前被調(diào)用 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{ // equalsIgnoreCase 與 equals的區(qū)別? 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){ // 跳轉(zhuǎn)到登錄頁面 request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response); return false; } else{ return true; } } // 在業(yè)務(wù)處理器處理請求完成之后,生成視圖之前執(zhí)行 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完全處理完請求之后被調(diào)用,可用于清理資源 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception{ System.out.println("afterCompletion..."); } }
攔截器是依賴Java反射機制來實現(xiàn)的。攔截器的實現(xiàn),用到的是JDK實現(xiàn)的動態(tài)代理,我們都知道,JDK實現(xiàn)的動態(tài)代理,需要依賴接口。
攔截器是在面向切面編程中應(yīng)用的,就是在你的service或者一個方法前調(diào)用一個方法,或者在方法后調(diào)用一個方法。攔截器不是在web.xml,比如struts在struts.xml中配置。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("方法調(diào)用前,可以執(zhí)行一段代碼" + method.getName()); result = method.invoke(this.targetObj, args); System.out.println("方法調(diào)用后,可以執(zhí)行一段代碼 " + method.getName()); return result; }
總結(jié):
- 1.過濾器(Filter):所謂過濾器顧名思義是用來過濾的,Java的過濾器能夠為我們提供系統(tǒng)級別的過濾,也就是說,能過濾所有的web請求,這一點,是攔截器無法做到的。在Java Web中,你傳入的request,response提前過濾掉一些信息,或者提前設(shè)置一些參數(shù),然后再傳入servlet或者struts的action進行業(yè)務(wù)邏輯,比如過濾掉非法url(不是login.do的地址請求,如果用戶沒有登陸都過濾掉),或者在傳入servlet或者struts的action前統(tǒng)一設(shè)置字符集,或者去除掉一些非法字符(聊天室經(jīng)常用到的,一些罵人的話)。filter 流程是線性的,url傳來之后,檢查之后,可保持原來的流程繼續(xù)向下執(zhí)行,被下一個filter, servlet接收。
- 2.監(jiān)聽器(Listener):Java的監(jiān)聽器,也是系統(tǒng)級別的監(jiān)聽。監(jiān)聽器隨web應(yīng)用的啟動而啟動。Java的監(jiān)聽器在c/s模式里面經(jīng)常用到,它會對特定的事件產(chǎn)生產(chǎn)生一個處理。監(jiān)聽在很多模式下用到,比如說觀察者模式,就是一個使用監(jiān)聽器來實現(xiàn)的,在比如統(tǒng)計網(wǎng)站的在線人數(shù)。又比如struts2可以用監(jiān)聽來啟動。Servlet監(jiān)聽器用于監(jiān)聽一些重要事件的發(fā)生,監(jiān)聽器對象可以在事情發(fā)生前、發(fā)生后可以做一些必要的處理。
- 3.攔截器(Interceptor):java里的攔截器提供的是非系統(tǒng)級別的攔截,也就是說,就覆蓋面來說,攔截器不如過濾器強大,但是更有針對性。Java中的攔截器是基于Java反射機制實現(xiàn)的,更準確的劃分,應(yīng)該是基于JDK實現(xiàn)的動態(tài)代理。它依賴于具體的接口,在運行期間動態(tài)生成字節(jié)碼。攔截器是動態(tài)攔截Action調(diào)用的對象,它提供了一種機制可以使開發(fā)者在一個Action執(zhí)行的前后執(zhí)行一段代碼,也可以在一個Action執(zhí)行前阻止其執(zhí)行,同時也提供了一種可以提取Action中可重用部分代碼的方式。在AOP中,攔截器用于在某個方法或者字段被訪問之前,進行攔截然后再之前或者之后加入某些操作。java的攔截器主要是用在插件上,擴展件上比如 Hibernate Spring Struts2等,有點類似面向切片的技術(shù),在用之前先要在配置文件即xml,文件里聲明一段的那個東西。
到此這篇關(guān)于Java攔截器Interceptor實現(xiàn)原理及代碼示例的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java中BeanUtils.copyProperties的用法(超詳細)
本文介紹了BeanUtils.copyProperties()方法的使用,包括其功能、用法、注意事項和示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08詳解spring整合shiro權(quán)限管理與數(shù)據(jù)庫設(shè)計
這篇文章主要介紹了詳解spring整合shiro權(quán)限管理與數(shù)據(jù)庫設(shè)計,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05