黄色网址大全免费-黄色网址你懂得-黄色网址你懂的-黄色网址有那些-免费超爽视频-免费大片黄国产在线观看

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節點LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 學習攻略 Java學習 Java過濾器鏈的原理分析

Java過濾器鏈的原理分析

更新時間:2022-08-10 12:36:09 來源:動力節點 瀏覽1425次

在很多Java Web項目中,我們會在web.xml中配置一些過濾器來攔截請求,比如下面的編碼過濾器來解決亂碼:

<過濾器>
	<filter-name>encodingFilter</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	<初始化參數>
		<param-name>編碼</param-name>
		<param-value>UTF-8</param-value>
	</init-param>
	<初始化參數>
		<param-name>forceEncoding</param-name>
		<param-value>真</param-value>
	</init-param>
	</過濾器>
<過濾器映射>
	<filter-name>encodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

誰調用了這些過濾器?誰組織了他們并確保了他們的執行順序?過濾器演示如下:

@WebFilter(filterName="FilterDemo", urlPatterns={"/**"})
@Order(1)//當有多個過濾器時,指定過濾器的順序
公共類 SecurityFilter 實現 Filter{
	private static final Logger LOGGER = LoggerFactory.getLogger(FilterDemo.class);	
	@Override
	公共無效銷毀(){		
	}
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			拋出 IOException,ServletException {
		//TODO 做一些過濾動作;
		鏈.doFilter(請求,響應);
	}
	@Override
	公共無效初始化(FilterConfig arg0)拋出 ServletException {
		//TODO 自動生成的方法存根	}
}

本以為這樣寫后過濾器會起作用,但是請求進來后,發現過濾器沒有執行。后來想到 Spring 的工作是由 beanFactry 中注冊的每個 bean 完成的,所以可能需要在 beanFactory 中注冊這個過濾器,像這樣:

@Configuration//表示這是一個Spring配置文件
@EnableSwagger2//swagger是一個restful接口文檔在線自動生成+功能測試功能框架
@RefreshScope//允許依賴配置的bean在Spring cloud config配置文件更改后無需重啟服務即可自動更新
公共類 GlobalBeanConfig {
   //將過濾器添加到Spring配置中,否則只會寫過濾器類,不添加配置不起作用
    @Bean//使用代碼配置xml形式的<bean></bean>
    公共過濾器 filterDemo(){
        返回新的 FilterDemo();
    }
    @Bean(name = "loggerInteceptor")
    公共 GlobalAspectInteceptor getLoggerInteceptor() {
        返回新的 GlobalAspectInteceptor();
    }
    @豆
    公共 ThreadPoolTask??Executor globalTask??Executor(
            @Value("${global.thread.pool.corePoolSize}") 整數 corePoolSize,
            @Value("${global.thread.pool.maxPoolSize}") 整數 maxPoolSize,
            @Value("${global.thread.pool.queueCapacity}") 整數 queueCapacity
    ) {
        ThreadPoolTask??Executor 執行器 = 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();
        返回執行者;
    }
}

添加評論后,它立即生效。既然知道了工具的使用方法,那我們就來拆解一下工具,看看效果如何。通過斷點調試發現過濾器中的兩個關鍵類在起作用:ApplicationFilterConfig和ApplicationFilterChain。

這兩個類在 org.apache.catalina.core 包下。可以肯定的是,tomcat 容器管理過濾器鏈。

接下來,我們看一下ApplicationFilterConfig,先上部分源碼:

Final 類 public ApplicationFilterConfig實現了 FilterConfig , Serializable {
      Private Long Final static serialVersionUID = 1L ;
 靜態最終StringManager SM = StringManager 。getManager(“org.apache.catalina.core”);
 私有靜態最終Log log = LogFactory 。getLog (
  ApplicationFilterConfig.class ) ; 私有靜態最終列表<字符串>空字符串=收藏??樟斜恚ǎ?;
 私有最終瞬態上下文上下文;
 私有瞬態過濾器 filter = null ;
 私人最終過濾器過濾器過濾器;
 私有瞬態InstanceManager instanceManager ;
 私有對象名oname ;
ApplicationFilterConfig(Context context , FilterDef filterDef) throws ClassCastException , ClassNotFoundException , IllegalAccessException , InstantiationException ,ServletException 、 InvocationTargetException 、 NamingException 、 IllegalArgumentException 、 NoSuchMethodException 、 SecurityException {
          this . 上下文=上下文;
 這個。過濾器定義=過濾器定義;
 IF (filterDef . getFilter () == null ) {
              this . getFilter () ;
}其他{
             這個.過濾器=過濾器定義。getFilter () ;
這個。獲取實例管理器()。新實例(這個。過濾器);
 這個。初始化過濾器();
}
    }
}

通過分析可以發現,這個類是用來保存一個過濾器的。構造方法傳入的上下文必須是tomcat配置文件下context.xml配置的應用環境。FliterDef是過濾器的描述信息,應該通過在web.xml(或其他地方)中配置過濾器參數來構造。

好了,重點來了——ApplicationFilterChain ,名字就透露了它的作用,就是它組織了分散的過濾器。結合上面我的猜測,它應該有一個數組或列表來保存ApplicationFilterConfig,以及一個過濾器光標來記錄當前過濾器去了哪里。源代碼(部分)如下:

public final 類ApplicationFilterChain實現FilterChain {
      private static final ThreadLocal < ServletRequest > lastServicedRequest ;
 private static final ThreadLocal < ServletResponse > lastServicedResponse ;
 公共靜態最終 int INCREMENT = 10 ;
 私有ApplicationFilterConfig[] 過濾器=新ApplicationFilterConfig[ 0 ] ;
 私人 int pos = 0 ;
 私人 int n = 0 ;
 私有Servlet servlet = null ;
 私有布爾servletSupportsAsync = false ;
 私有靜態最終StringManager sm ;
 private static final Class <?> [] classType ;
 私有靜態最終類<?> [] classTypeUsedInService ;
 公共應用程序過濾器鏈(){
    }
    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異常{
                        應用過濾鏈。這. 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) ;
}
        }其他{
             這個. internalDoFilter(請求,響應);
}
    }
    private void internalDoFilter(ServletRequest request , ServletResponse response) throws IOException , ServletException
          { if ( this.pos < this.n ) {
            ApplicationFilterConfig e1 =這個。過濾器[這個。位置++ ] ;
試試{
                過濾器 res1 = e1 。獲取過濾器();
 if (request . IsAsyncSupported() && "false" . EqualsIgnoreCase(e1 . GetFilterDef () . GetAsyncSupported ())) {
                    請求。setAttribute(“org.apache.catalina.ASYNC_SUPPORTED” ,布爾值。假);
}
                如果(全局。IS_SECURITY_ENABLED){
                    主體 principal1 = ((HttpServletRequest)request) 。獲取用戶主體();
Object[] args1 = new Object[]{request , response , this } ;
安全實用程序。DoAsPrivilege( "doFilter" , res1 , classType , args1 , principal1) ;
}其他{
                    水庫1 。doFilter(request , response , this ) ;
}
            } catch (ServletException | RuntimeException | IOException var15) {
                  throw var15 ;
}捕捉(可拋出的 var16){
                可拋出的res = ExceptionUtils 。UnwrapInvocationTargetException(var16) ;
異常實用程序。HandleThrowable(res) ;
 throw new ServletException( sm.GetString ( " filterChain.filter" ) , res) ;
        } else {
             嘗試{
                  if (ApplicationDispatcher . WRAP_SAME_OBJECT) {
                    最后服務請求。設置(請求);
最后服務響應。設置(響應);
}
                if (request .isAsyncSupported () &&! this .servletSupportsAsync ) {
                    請求。setAttribute(“org.apache.catalina.ASYNC_SUPPORTED” ,布爾值。假);
}
                如果( HttpServletRequest的請求實例&& HttpServletResponse的響應實例&&全局。IS_SECURITY_ENABLED){
                    主體主體= ((HttpServletRequest)request) 。獲取用戶主體();
Object[] args = new Object[]{request , response} ;
安全實用程序。DoAsPrivilege( "service "
 , this.Servlet , classTypeUsedInService , args , principal ) ; }其他{
                     這個. 小服務程序。服務(請求,響應);
}
            } catch (ServletException | RuntimeException | IOException var17) {
                  throw var17 ;
}捕捉(Throwable var18){
                可拋出的e = ExceptionUtils 。UnwrapInvocationTargetException(var18) ;
異常實用程序。HandleThrowable(e) ;
 throw new ServletException( sm.GetString ( " filterChain.servlet" ) , e) ;
}最后{
                  if (ApplicationDispatcher . WRAP_SAME_OBJECT) { 
                    lastServicedRequest 。設置((對象)空);
最后服務響應。設置((對象)空) ;
}
            }
        }
    }
    …… }

果然字段中有一個ApplicationFilterConfig[]用來存放一系列filter,pos用來存放當前filter位置,還有其他字段就不深入了。有興趣的朋友可以自行探索。

我們來看兩個關鍵方法:doFilter、internalDoFilter

doFilter的最終目的只有一個,調用internalDoFilter,中間可能會加一些安全策略,估計Globals.IS_SECURITY_ENABLE和是否開啟https服務有關,具體沒有仔細研究。

internalDoFilter的最終目的只有一個,就是調整當前pos指向的filter鏈中某個filter的doFilter(request, response, this)方法。中間可能會添加一些安全策略,當所有過濾器都被調用時,進行一些收尾工作,包括調用servlet.service(request, response)方法處理真正的請求,以及清除當前存儲的請求和響應threadLocal 為下一個請求做準備。

再梳理一下流程:

一個請求進來,先給自己給filterChain;

filterChain 啟動過濾器鏈,從頭開始,將請求交給第一個過濾器,并將自身傳遞給過濾器;

Filter在doFilter中完成自己的過濾邏輯,然后調用filterChain的doFilter開始下一個過濾器;

filterChain 光標移動,啟動下一個過濾器,依此類推...

過濾器光標走到鏈尾,filterChain進行收尾工作;

最后,給出一個簡單的流程圖:

提交申請后,顧問老師會電話與您溝通安排學習

免費課程推薦 >>
技術文檔推薦 >>
主站蜘蛛池模板: bbbb毛片免费看 | 成人黄色毛片 | 日韩激情视频在线观看 | 欧洲另类一二三四区 | 又黄又爽的美女免费视频 | 国产人成免费视频 | 国产一卡| 亚洲欧美卡通成人制服动漫 | 91中文字幕网 | 午夜h视频 | 一级毛片免费观看久 | 国产精品成人免费视频99 | 500福利笫一精品导航 | 中文字幕不卡一区 | 亚洲一区二区三区免费看 | 亚洲碰碰 | 欧美一区二区久久精品 | 97色噜噜刺激有声小说 | 亚洲欧美日韩国产精品 | 一级美国乱色毛片 | 久久国产午夜精品理论片34页 | 九九热九九热 | 日韩在线视频一区二区三区 | 91豆奶视频 | 日本tv欧美tv天堂 | 亚洲日本中文字幕天堂网 | 最近播放的好看hd | 欧美国产丝袜日韩精品 | 奇米影视亚洲狠狠色777不卡 | 亚洲一区日韩 | 亚洲日本1区2区3区二区 | 国产成人www免费人成看片 | 天天干天天拍天天射天天添天天爱 | 深夜释放自己糖心vlog | 夜夜躁狠狠躁日日躁视频 | 天堂网免费视频 | 污污网站免费入口链接 | 精品一区二区三区影院在线午夜 | 日韩一区二区三区视频 | yy4080午夜一毛片 | 很污的乱小说 |