Struts2 源碼分析——過濾器(Filter)


章節簡言

上一章筆者試着建一個Hello world的例子。是一個空白的struts2例子。明白了運行struts2至少需要用到哪一些Jar包。而這一章筆者將根據前面章節(Struts2 源碼分析——核心機制)里的機制圖片來分析源碼。如果還不明白核心機制的朋友,請轉到對應的章節進行閱讀。筆者為了方便讀者閱讀,也把圖片在次貼到了本章中。如下

根據圖片筆者就明白我們首要分析便是橙黃色(Servlet Filters)。也就是傳說的過濾器(Filter)。相信看過筆者前面幾個章節的讀者都明白struts2的配置方式有二種。即是StrutsPrepareFilter+StrutsExecuteFilter和StrutsPrepareAndExecuteFilter。不管是哪一種大部分都是一樣子。筆者用的是StrutsPrepareFilter+StrutsExecuteFilter來分析。那么讓我們看看關於他們到底做了些什么。Prepare意為“准備”。猜的沒有錯。StrutsPrepareFilter類就是為了開啟struts2之前加載一個相關的配置和執行的必要信息。同理,Execute意為“運行”。我們也就可以想像StrutsExecuteFilter類就是執行struts2。所謂分析就是要有一種敢於想像和猜測的心態。然后在證明就行了。

另外這里有聲明一下:筆者這里只想講一些有關struts2相關的知識。而像SiteMesh之類的筆者並不會深入。

StrutsPrepareFilter類的工作

StrutsPrepareFilter這個類必須在StrutsExecuteFilter類之前運行。否則就會出錯。當然struts2運行起來的時候,框架也有相關的提示你。那么先讓我們看一下代碼吧。如下

 1 package org.apache.struts2.dispatcher.filter;
 2 
 3 import org.apache.struts2.StrutsStatics;
 4 import org.apache.struts2.dispatcher.Dispatcher;
 5 import org.apache.struts2.dispatcher.InitOperations;
 6 import org.apache.struts2.dispatcher.PrepareOperations;
 7 
 8 import javax.servlet.Filter;
 9 import javax.servlet.FilterChain;
10 import javax.servlet.FilterConfig;
11 import javax.servlet.ServletException;
12 import javax.servlet.ServletRequest;
13 import javax.servlet.ServletResponse;
14 import javax.servlet.http.HttpServletRequest;
15 import javax.servlet.http.HttpServletResponse;
16 import java.io.IOException;
17 import java.util.List;
18 import java.util.regex.Pattern;
19 
20 public class StrutsPrepareFilter implements StrutsStatics, Filter {
21 
22     protected static final String REQUEST_EXCLUDED_FROM_ACTION_MAPPING = StrutsPrepareFilter.class.getName() + ".REQUEST_EXCLUDED_FROM_ACTION_MAPPING";
23 
24     protected PrepareOperations prepare;//用於每一次請求之前,執行一些功能的類。
25     protected List<Pattern> excludedPatterns = null;
26 
27     public void init(FilterConfig filterConfig) throws ServletException {
28         InitOperations init = new InitOperations();//用於初始化相關的功能操作。你可以理解為工具類一樣子。
29         Dispatcher dispatcher = null;//這個類相當的重要。他的作用連接着StrutsExecuteFilter。這里可以命名為調結者。
30         try {
31             FilterHostConfig config = new FilterHostConfig(filterConfig);//這里可以理解為把filterConfig在進行封裝FilterHostConfig更為主便操作和理解。
32             init.initLogging(config);//獲取名為loggerFactory的參數,並實例化這個類。一般為去用戶自定義日志。
33             dispatcher = init.initDispatcher(config);//初化調結者。這里是重要。
34 
35             prepare = new PrepareOperations(dispatcher);
36             this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);//加載排除在內的action的正則表達式
37 
38             postInit(dispatcher, filterConfig);
39         } finally {
40             if (dispatcher != null) {
41                 dispatcher.cleanUpAfterInit();
42             }
43             init.cleanup();
44         }
45     }
46 
47     protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
48     }
49 
50     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
51 
52         HttpServletRequest request = (HttpServletRequest) req;
53         HttpServletResponse response = (HttpServletResponse) res;
54 
55         try {
56             if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
57                 request.setAttribute(REQUEST_EXCLUDED_FROM_ACTION_MAPPING, new Object());
58             } else {
59                 prepare.setEncodingAndLocale(request, response);//設置請求的格式編碼。
60                 prepare.createActionContext(request, response);//action的上下文
61                 prepare.assignDispatcherToThread();//把Dispatcher放入本地線程里面。
62                 request = prepare.wrapRequest(request);
63                 prepare.findActionMapping(request, response);//找到action映射的信息
64             }
65             chain.doFilter(request, response);
66         } finally {
67             prepare.cleanupRequest(request);
68         }
69     }
70 
71     public void destroy() {
72         prepare.cleanupDispatcher();
73     }
74 
75 }
StrutsPrepareFilter 類的源碼

上面的源碼也做了一些簡單的注解。當然這是筆者的理解。如果你看了源碼覺得筆者理解的不對。沒事!只要讀者自己心里面明白原理的話就可以了。我們可以看到了好多的類,對於筆者來講以前有過經驗,看起來比較輕松。但是對於四年前初學者的我來講,那真是天書。筆者一個一個解釋給讀者聽也不現實。這里筆者只把主要的相關類拿出來講解。希望讀者們見諒!StrutsPrepareFilter類現實於接口StrutsStatics。這接口都是大量常量。而StrutsPrepareFilter類本身有二個成員變量。其中一個成員變量prepare(PrepareOperations類)的工作如下。

prepare成員變量的工作:

1.設置request請求的本地化。即是:本地語言和編碼格式。

2.創建一個新的action上下文。對於上下文不理解的讀者可以查看相關的文章。如果不行的話,筆者認為你可以理解為進入房間的門一樣子。action請求則是房間。新建一個房間就有一個新的門。新action請求就有一個新的action上下文。

3.把當前的Dispatcher實例存放到當前的本地線程(ThreadLocal)中。而Dispatcher類是一個重要的核心類,struts2的action請求就是靠他來執行的。(對於Dispatcher類的作用不理解的話。沒有事。后面我會對Dispatcher進行講解)

4.把HttpServletRequest請求封裝成為StrutsRequestWrapper或是MultiPartRequestWrapper。可以說這部分的工作也是靠Dispatcher實例來執行的。

5.找到action映射類(ActionMapping)的實例。並把他存在到request請求里面。他對應的Key為“struts.actionMapping”; 讀者會問ActionMapping類是什么東東。現在可以理解為里面存放用戶action的配置信息。大白話就是用戶在地址欄上輸入URL找到對應的action類。

以上是prepare成員變量的工作,他是主要目的就是根據request請求找到對應action映射。以便於StrutsExecuteFilter類根據action映射類里面的信息找到對應的用戶action類,並執行。從這里筆者就可以明顯感覺出來,StrutsPrepareFilter類是執行action請求之前的相關准備工作。那么敏感的讀者就會問:“正常在這之前應該會加載或初始化相關的配置信息才對啊?不然他后面執行action請求什么工作呢?”。沒有錯。讓我們看一下過濾器(Filter)的方法init吧。可以明確的指出加載相關的配置信息就在這里進行的。他的工作如下

init方法:

1.查看用戶是否有自定義日志類。如果有,初始化並實例用戶定義的日志類。存放到LoggerFactory類里面。LoggerFactory類里面用的是單例模式。

2.實例化Dispatcher類,並初始化加載相關的配置的信息文件。如 default.properties文件,struts-default.xml文件等等。

3.實例化PrepareOperations類,Dispatcher實例存放進去。為之后的request請求工作做准備。即是上面PrepareOperations類所講的。

4.加載用戶自定義不應該被外部訪問的action相對應的正則表達式。這邊也就是StrutsPrepareFilter類里面的另一個成員變量。

正如上述所講的就是StrutsPrepareFilter類的工作。簡單點講就是為action請求執行之前做好一切准備的類。其中init方法就是用於加載相關配置文件,初始化信息的工作。而PrepareOperations類是用於request請求的處理。其中包設置格式,找對應的action映射類等等操作。即是ActionMapping類。

StrutsExecuteFilter類的工作

上面講到StrutsPrepareFilter類的工作,那么對於StrutsExecuteFilter類的工作就顯得很簡單。就是執行action請求。讓我們先看一下代碼吧。如下

 1 package org.apache.struts2.dispatcher.filter;
 2 
 3 import org.apache.struts2.StrutsStatics;
 4 import org.apache.struts2.dispatcher.Dispatcher;
 5 import org.apache.struts2.dispatcher.mapper.ActionMapping;
 6 import org.apache.struts2.dispatcher.ExecuteOperations;
 7 import org.apache.struts2.dispatcher.InitOperations;
 8 import org.apache.struts2.dispatcher.PrepareOperations;
 9 
10 import javax.servlet.*;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13 import java.io.IOException;
14 
15 public class StrutsExecuteFilter implements StrutsStatics, Filter {
16     protected PrepareOperations prepare;//用於每一次請求之前,執行一些功能的類。
17     protected ExecuteOperations execute;//用於執行請求的功能類。
18 
19     protected FilterConfig filterConfig;
20 
21     public void init(FilterConfig filterConfig) throws ServletException {
22         this.filterConfig = filterConfig;
23     }
24 
25     protected synchronized void lazyInit() {
26         if (execute == null) {
27             InitOperations init = new InitOperations();//用於初始化的功能類
28             Dispatcher dispatcher = init.findDispatcherOnThread();
29             init.initStaticContentLoader(new FilterHostConfig(filterConfig), dispatcher);
30 
31             prepare = new PrepareOperations(dispatcher);
32             execute = new ExecuteOperations(dispatcher);
33         }
34 
35     }
36 
37     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
38 
39         HttpServletRequest request = (HttpServletRequest) req;
40         HttpServletResponse response = (HttpServletResponse) res;
41 
42         if (excludeUrl(request)) {//用於判斷是否在排除的action之內。如果是就跳過。
43             chain.doFilter(request, response);
44             return;
45         }
46 
47         if (execute == null) {
48             lazyInit();//初始化相關的信息類。
49         }
50 
51         ActionMapping mapping = prepare.findActionMapping(request, response);//找到ActionMapping實例
52 
53   
54         Integer recursionCounter = (Integer) request.getAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER);
55 
56         if (mapping == null || recursionCounter > 1) {
57             boolean handled = execute.executeStaticResourceRequest(request, response);
58             if (!handled) {
59                 chain.doFilter(request, response);
60             }
61         } else {
62             execute.executeAction(request, response, mapping);//執行action請求
63         }
64     }
65 
66     private boolean excludeUrl(HttpServletRequest request) {
67         return request.getAttribute(StrutsPrepareFilter.REQUEST_EXCLUDED_FROM_ACTION_MAPPING) != null;
68     }
69 
70     public void destroy() {
71         prepare = null;
72         execute = null;
73         filterConfig = null;
74     }
75 
76 }
StrutsExecuteFilter類的源碼

筆者在這個類上的注解比較簡單,主要是筆者不知道這個類什么樣子去講。因為這個類比StrutsPrepareFilter類來講簡單多了。工作也很單一。所以筆者一下子不知道要什么樣子去注解。筆者認為這個類的重點有二個地方。一是lazyInit方法,二是ExecuteOperations類的工作。而其中lazyInit方法主要是用於初始化相關需要的類。並沒有值得注意的點。那么為什么筆者卻要說他是重點之一呢?問題在於他還有一個工作是初始化靜態內容加載器(StaticContentLoader類)。可惜不是本章的重點。所以筆者要講的是ExecuteOperations類的工作。如下

ExecuteOperations類的工作:

1.組裝相關的Map類。如requestMap,params,session 等。

2.找到ActionProxy類。該類是用於執行action請求的。也是關鍵的類。(后面章節會講到)

3.組裝action請求執行的結果。也是關鍵的類。(后面章節會講到)

StrutsExecuteFilter類的工作目前只需要知道他是執行action請求的。如果讀者不明白不要擔心。筆者后面會講到。

本章總結

本章的重點並不是要知道如何去執行action請求。而是知道在執行action請求之前要做些什么工作。只要明白了這一點我們就知道目標是什么。所以在本章筆者在講StrutsPrepareFilter類的時候,講的比較多。就是要讓讀者明白准備工作都有哪些什么。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM