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

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

Java過濾器鏈的原理分析

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

在很多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進行收尾工作;

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

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

免費課程推薦 >>
技術文檔推薦 >>
主站蜘蛛池模板: 一个人看的www片免费中文 | 日本系列第一页 | 日韩特级毛片免费观看视频 | 欧美日韩一日韩一线不卡 | 亚洲午夜影视 | 午夜www| 天天爽夜夜爽8888视频精品 | 手机小视频在线观看 | 天天插天天狠天天透 | 欧美成人精品高清在线观看 | 亚洲欧美日韩精品久久亚洲区 | 成 人 黄 色 视频免费播放 | 中文字幕午夜乱理片11111 | 日韩欧美视频 | 夜夜春精品视频 | 欧美日韩亚洲天堂 | 精品日韩在线视频 | 国产专区在线视频 | 男女后进式猛烈xx00动态图片 | 欧美成人免费观看久久 | 日韩中文字幕视频在线 | 日韩永久免费视频 | 白洁的性荡生活全文 | 日日做夜夜爽夜夜爽 | 国产成人一区二区三区在线播放 | 久久婷婷是五月综合色狠狠 | 91精品国产91久久久久久青草 | 日本道在线播放 | 免费一级毛片 | 久久精品视屏 | 免费被黄网站在观看 | 日韩字幕| 亚洲人成在线观看 | 最近中文2019视频在线 | 亚洲欧美日韩国产专区一区 | 国产乱人伦av在线a 国产乱人伦精品一区二区 国产乱人免费视频 | 国产精品成人一区二区三区 | 大学生一级毛片全黄真人 | 国产成人精品久久免费动漫 | 国产成人99精品免费观看 | 国产黑色丝袜小视频在线 |