更新時(shí)間:2022-08-10 12:36:09 來源:動(dòng)力節(jié)點(diǎn) 瀏覽1310次
在很多Java Web項(xiàng)目中,我們會(huì)在web.xml中配置一些過濾器來攔截請(qǐng)求,比如下面的編碼過濾器來解決亂碼:
<過濾器>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<初始化參數(shù)>
<param-name>編碼</param-name>
<param-value>UTF-8</param-value>
</init-param>
<初始化參數(shù)>
<param-name>forceEncoding</param-name>
<param-value>真</param-value>
</init-param>
</過濾器>
<過濾器映射>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
誰調(diào)用了這些過濾器?誰組織了他們并確保了他們的執(zhí)行順序?過濾器演示如下:
@WebFilter(filterName="FilterDemo", urlPatterns={"/**"})
@Order(1)//當(dāng)有多個(gè)過濾器時(shí),指定過濾器的順序
公共類 SecurityFilter 實(shí)現(xiàn) Filter{
private static final Logger LOGGER = LoggerFactory.getLogger(FilterDemo.class);
@Override
公共無效銷毀(){
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
拋出 IOException,ServletException {
//TODO 做一些過濾動(dòng)作;
鏈.doFilter(請(qǐng)求,響應(yīng));
}
@Override
公共無效初始化(FilterConfig arg0)拋出 ServletException {
//TODO 自動(dòng)生成的方法存根 }
}
本以為這樣寫后過濾器會(huì)起作用,但是請(qǐng)求進(jìn)來后,發(fā)現(xiàn)過濾器沒有執(zhí)行。后來想到 Spring 的工作是由 beanFactry 中注冊(cè)的每個(gè) bean 完成的,所以可能需要在 beanFactory 中注冊(cè)這個(gè)過濾器,像這樣:
@Configuration//表示這是一個(gè)Spring配置文件
@EnableSwagger2//swagger是一個(gè)restful接口文檔在線自動(dòng)生成+功能測(cè)試功能框架
@RefreshScope//允許依賴配置的bean在Spring cloud config配置文件更改后無需重啟服務(wù)即可自動(dòng)更新
公共類 GlobalBeanConfig {
//將過濾器添加到Spring配置中,否則只會(huì)寫過濾器類,不添加配置不起作用
@Bean//使用代碼配置xml形式的<bean></bean>
公共過濾器 filterDemo(){
返回新的 FilterDemo();
}
@Bean(name = "loggerInteceptor")
公共 GlobalAspectInteceptor getLoggerInteceptor() {
返回新的 GlobalAspectInteceptor();
}
@豆
公共 ThreadPoolTask??Executor globalTask??Executor(
@Value("${global.thread.pool.corePoolSize}") 整數(shù) corePoolSize,
@Value("${global.thread.pool.maxPoolSize}") 整數(shù) maxPoolSize,
@Value("${global.thread.pool.queueCapacity}") 整數(shù) queueCapacity
) {
ThreadPoolTask??Executor 執(zhí)行器 = new ThreadPoolTask??Executor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadGroupName("globalTask??Executor");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.initialize();
返回執(zhí)行者;
}
}
添加評(píng)論后,它立即生效。既然知道了工具的使用方法,那我們就來拆解一下工具,看看效果如何。通過斷點(diǎn)調(diào)試發(fā)現(xiàn)過濾器中的兩個(gè)關(guān)鍵類在起作用:ApplicationFilterConfig和ApplicationFilterChain。
這兩個(gè)類在 org.apache.catalina.core 包下。可以肯定的是,tomcat 容器管理過濾器鏈。
接下來,我們看一下ApplicationFilterConfig,先上部分源碼:
Final 類 public ApplicationFilterConfig實(shí)現(xiàn)了 FilterConfig , Serializable {
Private Long Final static serialVersionUID = 1L ;
靜態(tài)最終StringManager SM = StringManager 。getManager(“org.apache.catalina.core”);
私有靜態(tài)最終Log log = LogFactory 。getLog (
ApplicationFilterConfig.class ) ; 私有靜態(tài)最終列表<字符串>空字符串=收藏。空列表();
私有最終瞬態(tài)上下文上下文;
私有瞬態(tài)過濾器 filter = null ;
私人最終過濾器過濾器過濾器;
私有瞬態(tài)InstanceManager instanceManager ;
私有對(duì)象名oname ;
ApplicationFilterConfig(Context context , FilterDef filterDef) throws ClassCastException , ClassNotFoundException , IllegalAccessException , InstantiationException ,ServletException 、 InvocationTargetException 、 NamingException 、 IllegalArgumentException 、 NoSuchMethodException 、 SecurityException {
this . 上下文=上下文;
這個(gè)。過濾器定義=過濾器定義;
IF (filterDef . getFilter () == null ) {
this . getFilter () ;
}其他{
這個(gè).過濾器=過濾器定義。getFilter () ;
這個(gè)。獲取實(shí)例管理器()。新實(shí)例(這個(gè)。過濾器);
這個(gè)。初始化過濾器();
}
}
}
通過分析可以發(fā)現(xiàn),這個(gè)類是用來保存一個(gè)過濾器的。構(gòu)造方法傳入的上下文必須是tomcat配置文件下context.xml配置的應(yīng)用環(huán)境。FliterDef是過濾器的描述信息,應(yīng)該通過在web.xml(或其他地方)中配置過濾器參數(shù)來構(gòu)造。
好了,重點(diǎn)來了——ApplicationFilterChain ,名字就透露了它的作用,就是它組織了分散的過濾器。結(jié)合上面我的猜測(cè),它應(yīng)該有一個(gè)數(shù)組或列表來保存ApplicationFilterConfig,以及一個(gè)過濾器光標(biāo)來記錄當(dāng)前過濾器去了哪里。源代碼(部分)如下:
public final 類ApplicationFilterChain實(shí)現(xiàn)FilterChain {
private static final ThreadLocal < ServletRequest > lastServicedRequest ;
private static final ThreadLocal < ServletResponse > lastServicedResponse ;
公共靜態(tài)最終 int INCREMENT = 10 ;
私有ApplicationFilterConfig[] 過濾器=新ApplicationFilterConfig[ 0 ] ;
私人 int pos = 0 ;
私人 int n = 0 ;
私有Servlet servlet = null ;
私有布爾servletSupportsAsync = false ;
私有靜態(tài)最終StringManager sm ;
private static final Class <?> [] classType ;
私有靜態(tài)最終類<?> [] classTypeUsedInService ;
公共應(yīng)用程序過濾器鏈(){
}
public void doFilter(ServletRequest request , ServletResponse response) throws IOException , ServletException {
if (Globals . IS_SECURITY_ENABLED) {
final ServletRequest req = request ;
最終的 ServletResponse res = response ;
嘗試{
AccessController 。DoPrivileged( new PrivilegedExceptionAction() {
public Void run() throws ServletException ,IO異常{
應(yīng)用過濾鏈。這. InternalDoFilter(req , res) ;
返回空值;
} }) ;
}捕捉(PrivilegedActionException var7){
異常 e = var7 。獲取異常 () ;
if (e instanceof ServletException) {
throw (ServletException)e ;
}
if (e instanceof IOException) {
throw (IOException)e ;
}
if (e instanceof RuntimeException) {
throw (RuntimeException)e ;
}
throw new ServletException(e .getMessage () , e) ;
}
}其他{
這個(gè). internalDoFilter(請(qǐng)求,響應(yīng));
}
}
private void internalDoFilter(ServletRequest request , ServletResponse response) throws IOException , ServletException
{ if ( this.pos < this.n ) {
ApplicationFilterConfig e1 =這個(gè)。過濾器[這個(gè)。位置++ ] ;
試試{
過濾器 res1 = e1 。獲取過濾器();
if (request . IsAsyncSupported() && "false" . EqualsIgnoreCase(e1 . GetFilterDef () . GetAsyncSupported ())) {
請(qǐng)求。setAttribute(“org.apache.catalina.ASYNC_SUPPORTED” ,布爾值。假);
}
如果(全局。IS_SECURITY_ENABLED){
主體 principal1 = ((HttpServletRequest)request) 。獲取用戶主體();
Object[] args1 = new Object[]{request , response , this } ;
安全實(shí)用程序。DoAsPrivilege( "doFilter" , res1 , classType , args1 , principal1) ;
}其他{
水庫1 。doFilter(request , response , this ) ;
}
} catch (ServletException | RuntimeException | IOException var15) {
throw var15 ;
}捕捉(可拋出的 var16){
可拋出的res = ExceptionUtils 。UnwrapInvocationTargetException(var16) ;
異常實(shí)用程序。HandleThrowable(res) ;
throw new ServletException( sm.GetString ( " filterChain.filter" ) , res) ;
} else {
嘗試{
if (ApplicationDispatcher . WRAP_SAME_OBJECT) {
最后服務(wù)請(qǐng)求。設(shè)置(請(qǐng)求);
最后服務(wù)響應(yīng)。設(shè)置(響應(yīng));
}
if (request .isAsyncSupported () &&! this .servletSupportsAsync ) {
請(qǐng)求。setAttribute(“org.apache.catalina.ASYNC_SUPPORTED” ,布爾值。假);
}
如果( HttpServletRequest的請(qǐng)求實(shí)例&& HttpServletResponse的響應(yīng)實(shí)例&&全局。IS_SECURITY_ENABLED){
主體主體= ((HttpServletRequest)request) 。獲取用戶主體();
Object[] args = new Object[]{request , response} ;
安全實(shí)用程序。DoAsPrivilege( "service "
, this.Servlet , classTypeUsedInService , args , principal ) ; }其他{
這個(gè). 小服務(wù)程序。服務(wù)(請(qǐng)求,響應(yīng));
}
} catch (ServletException | RuntimeException | IOException var17) {
throw var17 ;
}捕捉(Throwable var18){
可拋出的e = ExceptionUtils 。UnwrapInvocationTargetException(var18) ;
異常實(shí)用程序。HandleThrowable(e) ;
throw new ServletException( sm.GetString ( " filterChain.servlet" ) , e) ;
}最后{
if (ApplicationDispatcher . WRAP_SAME_OBJECT) {
lastServicedRequest 。設(shè)置((對(duì)象)空);
最后服務(wù)響應(yīng)。設(shè)置((對(duì)象)空) ;
}
}
}
}
…… }
果然字段中有一個(gè)ApplicationFilterConfig[]用來存放一系列filter,pos用來存放當(dāng)前filter位置,還有其他字段就不深入了。有興趣的朋友可以自行探索。
我們來看兩個(gè)關(guān)鍵方法:doFilter、internalDoFilter
doFilter的最終目的只有一個(gè),調(diào)用internalDoFilter,中間可能會(huì)加一些安全策略,估計(jì)Globals.IS_SECURITY_ENABLE和是否開啟https服務(wù)有關(guān),具體沒有仔細(xì)研究。
internalDoFilter的最終目的只有一個(gè),就是調(diào)整當(dāng)前pos指向的filter鏈中某個(gè)filter的doFilter(request, response, this)方法。中間可能會(huì)添加一些安全策略,當(dāng)所有過濾器都被調(diào)用時(shí),進(jìn)行一些收尾工作,包括調(diào)用servlet.service(request, response)方法處理真正的請(qǐng)求,以及清除當(dāng)前存儲(chǔ)的請(qǐng)求和響應(yīng)threadLocal 為下一個(gè)請(qǐng)求做準(zhǔn)備。
再梳理一下流程:
一個(gè)請(qǐng)求進(jìn)來,先給自己給filterChain;
filterChain 啟動(dòng)過濾器鏈,從頭開始,將請(qǐng)求交給第一個(gè)過濾器,并將自身傳遞給過濾器;
Filter在doFilter中完成自己的過濾邏輯,然后調(diào)用filterChain的doFilter開始下一個(gè)過濾器;
filterChain 光標(biāo)移動(dòng),啟動(dòng)下一個(gè)過濾器,依此類推...
過濾器光標(biāo)走到鏈尾,filterChain進(jìn)行收尾工作;
最后,給出一個(gè)簡(jiǎn)單的流程圖:
相關(guān)閱讀
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
有基礎(chǔ) 直達(dá)就業(yè)
業(yè)余時(shí)間 高薪轉(zhuǎn)行
工作1~3年,加薪神器
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)
初級(jí) 202925
初級(jí) 203221
初級(jí) 202629
初級(jí) 203743