本文將用簡潔的代碼構(gòu)建一個springboot的攔截器。攔截器的使用很簡單,定義一個自己的攔截器,向配置中添加一下就可以使用。為了方便,之后又引入了注解。
克拉瑪依網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設計、網(wǎng)站建設、微信開發(fā)、APP開發(fā)、響應式網(wǎng)站開發(fā)等網(wǎng)站項目制作,到程序開發(fā),運營維護。創(chuàng)新互聯(lián)于2013年創(chuàng)立到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設就選創(chuàng)新互聯(lián)。
目錄和概述
概述
假設需求:訪問項目的controller是都要進行"token驗證",除了某些像登錄之類的方法。
項目結(jié)構(gòu):
TokenInterceptor.java 自定義攔截器
InterceptorConfig.java 添加攔截器進入項目
NoNeedToken.java 自定義注解
TestController.java 測試接口
1、自定義攔截器
在 TokenInterceptor.java 中輸入以下代碼,以下的代碼將生成一個在請求到達controller前進行攔截的攔截器
import com.alibaba.fastjson.JSONObject;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
@Component
public class TokenInterceptor implements HandlerInterceptor {undefined
// 假設現(xiàn)在的token有如下數(shù)據(jù)
List tokenList = Arrays.asList("111", "222", "333");
// 這個方法是在訪問接口之前執(zhí)行的,我們只需要在這里寫驗證登陸狀態(tài)的業(yè)務邏輯,就可以在用戶調(diào)用指定接口之前驗證登陸狀態(tài)了
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {undefined
// 設置返回為json格式,使用UTF-8
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
String token = request.getHeader("token");
PrintWriter out;
// 之后寫你的判斷邏輯:return true是通過攔截器,可以繼續(xù)訪問controller,return false是不通過
if (token == null || !tokenList.contains(token)) {undefined
// 如果失敗了返回{state:"false", msg:"token is null or wrong"}
JSONObject res = new JSONObject();
res.put("state","false");
res.put("msg","token is null or wrong");
out = response.getWriter();
out.append(res.toString());
return false;
}
// 否則返回true 進入controller
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {undefined
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {undefined
}
}
————————————————
版權(quán)聲明:本文為CSDN博主「魔王別囂張」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:
一個Filter可以附加到一個或多個Servlet上,一個Servlet可以附加一個或多個Filter。
Filter的生命周期為:init()→doFilter()→destroy()
一個filter必須實現(xiàn)javax.servlet.Filter接口并定義這3個方法:
init():這個方法在容器實例化過濾器時被調(diào)用,它主要用于使過濾器為處理做準備。
doFilter():這個方法用于處理請求和響應。此方法是Filter的核心
destroy():該方法由容器在銷毀過濾器實例之前調(diào)用。具體代碼如下:
import java.io.IOException;import javax.servlet.Filter;
import javax.servlet.FilterChain;import javax.servlet.FilterConfig;
import javax.servlet.ServletException;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class CharsetEncodingFilter implements Filter {
public void destroy() { }
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//設置字符集
servletRequest.setCharacterEncoding("GB18030");
filterChain.doFilter(servletRequest, servletResponse);
}
public void init(FilterConfig filterConfig) throws ServletException { }
}
還要在web.xml文件中配置:
filter
filter-nameCharsetEncodingFilter/filter-name !--Filter名稱--
filter-classcom.wang.drp.util.filter.CharsetEncodingFilter/filter-class !--Filter類文件地址映射--
/filter
filter-mapping
filter-nameCharsetEncodingFilter/filter-name !--Filter名稱,要和上面的一致--
url-pattern*.jsp/url-pattern !--Filter需要過濾的文件資源--
/filter-mapping
在Java的服務端開發(fā)當中,攔截器是很常見的業(yè)務場景,這里對Java開發(fā)當中幾種常見的攔截器的實現(xiàn)方式進行記錄和分析。案例說明基于Spring Boot環(huán)境。
一:實現(xiàn)javax.servlet.Filter接口(使用過濾器方式攔截請求)
import org.springframework.stereotype.Component;import javax.servlet.*;import java.io.IOException;import java.util.Date;@Componentpublic class TimeInterceptor implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("time filter init");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("time filter start");long start = new Date().getTime();filterChain.doFilter(servletRequest, servletResponse);System.out.println("time filter 耗時:"+(new Date().getTime()-start));System.out.println("time filter finish");}@Overridepublic void destroy() {System.out.println("time filter destroy");}}
如使用@Compent注解聲明不需要加入其它配置即可使得攔截器生效,但是默認攔截/*,會攔截所有請求。
二:使用@Bean注入自定義攔截器,依然上面的代碼,去掉@Compent注解,創(chuàng)建TimeWebConfig配置類:
import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.ArrayList;import java.util.List;@Configurationpublic class TimeWebConfig {@Beanpublic FilterRegistrationBean timeFilter(){FilterRegistrationBean registrationBean = new FilterRegistrationBean();TimeInterceptor interceptor = new TimeInterceptor();registrationBean.setFilter(interceptor);ListString urls = new ArrayList();urls.add("/user/*");registrationBean.setUrlPatterns(urls);return registrationBean;}}
上面這兩種攔截請求的實現(xiàn)是基于JavaEE提供的Filter接口實現(xiàn)的,缺點在于,該攔截器實際上是一個過濾器,執(zhí)行代碼的方法doFilter只提供了request,response等參數(shù),當請求進入被過濾器攔截的時候,我們并不知道這個請求是由哪個控制器的哪個方法來執(zhí)行的。
三:使用springMVC提供的攔截器,實現(xiàn)org.springframework.web.servlet.HandlerInterceptor接口:
創(chuàng)建自定義的攔截器:
import org.springframework.stereotype.Component;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.Date;@Componentpublic class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {System.out.println("preHandler");System.out.println(((HandlerMethod) handler).getBean().getClass().getName());System.out.println(((HandlerMethod) handler).getMethod().getName());httpServletRequest.setAttribute("start", new Date().getTime());return true;}@Overridepublic void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {System.out.println("postHandler");Long start = (Long) httpServletRequest.getAttribute("start");System.out.println("time interceptor 耗時:"+(new Date().getTime()-start));}@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {System.out.println("afterCompletion");Long start = (Long) httpServletRequest.getAttribute("start");System.out.println("time interceptor 耗時:"+(new Date().getTime()-start));System.out.println("ex is:"+e);}}
創(chuàng)建配置類:
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;@Configurationpublic class WebConfig extends WebMvcConfigurerAdapter {@Autowiredprivate MyInterceptor interceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(interceptor).addPathPatterns("/user/*").excludePathPatterns("/blog/*");}}
此種方式的攔截器當中我們能夠獲取攔截的請求對應的類和方法的相關(guān)信息,缺點在于該handler對象無法獲取具體執(zhí)行方法的參數(shù)信息。
四:利用Spring的切面(AOP)實現(xiàn)攔截器:
引入jar包:
!-- --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId/dependency
創(chuàng)建切片類:
import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.springframework.stereotype.Component;import java.util.Date;@Aspect@Componentpublic class TimeAspect {@Around("execution(* com.qinker.controller.UserController.*(..))")public Object handlerControllerMethod(ProceedingJoinPoint point) throws Throwable {System.out.println("time aspect start");long start = new Date().getTime();Object[] args = point.getArgs();for (Object obj : args) {System.out.println("arg is:"+obj);}Object obj = point.proceed();//具體方法的返回值System.out.println("aspect 耗時:"+(new Date().getTime()-start));System.out.println("time aspect end");return obj;}}
aspectj基于AOP實現(xiàn)的攔截器功能十分強大,具體詳解請參考spring官網(wǎng)網(wǎng)站的文檔。
java里的攔截器是動態(tài)攔截Action調(diào)用的對象,它提供了一種機制可以使開發(fā)者在一個Action執(zhí)行的前后執(zhí)行一段代碼,也可以在一個Action
執(zhí)行前阻止其執(zhí)行,同時也提供了一種可以提取Action中可重用部分代碼的方式。在AOP中,攔截器用于在某個方法或者字段被訪問之前,進行攔截
然后再之前或者之后加入某些操作。目前,我們需要掌握的主要是Spring的攔截器,Struts2的攔截器不用深究,知道即可。
2,攔截器的原理
大部分時候,攔截器方法都是通過代理的方式來調(diào)用的。Struts2的攔截器實現(xiàn)相對簡單。當請求到達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攔截器
攔截器在在流行的開源框架中很常見 依賴的技術(shù)就是Java的動態(tài)代理
理解攔截器的核心原理對理解這些開源框架的體系結(jié)構(gòu)至關(guān)重要
下面以一個簡單的模型的來說明攔截器的實現(xiàn)的一般方法
模型分為以下模塊
業(yè)務組件 是被代理和被攔截的對象
代理處理器 實現(xiàn)了InvocationHandler接口的一個對象
代理對象 Proxy對象
攔截器 普通的JavaBean 在調(diào)用業(yè)務方法的之前或者之后會自動攔截并執(zhí)行自己的一些方法
客戶端 執(zhí)行業(yè)務處理的入口
以下是模型的實現(xiàn)
一 業(yè)務組件 分為業(yè)務接口和業(yè)務類
/**
* 業(yè)務組件接口
*/
public interface BusinessInterface {
public void doSomething();
}
/**
* 業(yè)務組件
*/
public class BusinessClass implements BusinessInterface{
public void doSomething() {
System out println( 業(yè)務組件BusinessClass方法調(diào)用:doSomething() );
}
}
二 代理處理器 包含了業(yè)務對象綁定動態(tài)代理類的處理 并實現(xiàn)了 InvocationHandler接口的invoke方法
import java lang reflect InvocationHandler;
import java lang reflect Method;
import java lang reflect Proxy;
/**
* 動態(tài)代理處理器工具
*/
public class DynamicProxyHandler implements InvocationHandler {
private Object business;??? //被代理對象
private InterceptorClass interceptor = new InterceptorClass();??? //攔截器
/**
* 動態(tài)生成一個代理類對象 并綁定被代理類和代理處理器
*
* @param business
* @return 代理類對象
*/
public Object bind(Object business) {
this business = business;
return Proxy newProxyInstance(
//被代理類 的ClassLoader
business getClass() getClassLoader()
//要被代理 的接口 本方法返回對象會自動聲稱實現(xiàn)了這些接口
business getClass() getInterfaces()
//代理處理 器對象
this);
}
/**
* 代理要調(diào)用的方法 并在方法調(diào)用前后調(diào)用連接器的方法
*
* @param proxy? 代理類對象
* @param method 被代理的接口方法
* @param args?? 被代理接口方法的參數(shù)
* @return 方法調(diào)用返回的結(jié)果
* @throws Throwable
*/
public Object invoke(Object proxy Method method Object[] args) throws Throwable {
Object result = null;
interceptor before();
result=method invoke(business args);
interceptor after();
return null;? //To change body of implemented methods use File | Settings | File Templates
}
}
三 攔截器 普通的JavaBean 在調(diào)用業(yè)務方法的之前或者之后會自動攔截并執(zhí)行自己的 一些方法
/**
* 攔截器
*/
public class InterceptorClass {
public void before(){
System out println( 攔截器InterceptorClass方法調(diào)用:before()! );
}
public void after(){
System out println( 攔截器InterceptorClass方法調(diào)用:after()! );
}
}
四 模擬客戶端 執(zhí)行業(yè)務處理的入口
/**
* 客戶端
*/
public class Client {
public static void main(String args[]) {
DynamicProxyHandler handler = new DynamicProxyHandler();
BusinessInterface business = new BusinessClass();
BusinessInterface businessProxy = (BusinessInterface) handler bind(business);
businessProxy doSomething();
}
lishixinzhi/Article/program/Java/hx/201311/27143