更新時間: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進行收尾工作;
最后,給出一個簡單的流程圖:
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習