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

章節簡言

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

根據圖片筆者就明白咱們首要分析即是橙黃色(Servlet Filters)。也就是傳說的過濾器(Filter)。相信看過筆者前面幾個章節的讀者都明白struts2的配置方式有二種。便是StrutsPrepareFilter+StrutsExecuteFilter和StrutsPrepareAndExecuteFilter。不論是哪種大部分都是同樣子。筆者用的是StrutsPrepareFilter+StrutsExecuteFilter來分析。那麼讓咱們看看關於他們到底作了些什麼。Prepare意爲「準備」。猜的沒有錯。StrutsPrepareFilter類就是爲了開啓struts2以前加載一個相關的配置和執行的必要信息。同理,Execute意爲「運行」。咱們也就能夠想像StrutsExecuteFilter類就是執行struts2。所謂分析就是要有一種勇於想像和猜想的心態。而後在證實就好了。java

另外這裏有聲明一下:筆者這裏只想講一些有關struts2相關的知識。而像SiteMesh之類的筆者並不會深刻。正則表達式

StrutsPrepareFilter類的工做

StrutsPrepareFilter這個類必須在StrutsExecuteFilter類以前運行。不然就會出錯。固然struts2運行起來的時候,框架也有相關的提示你。那麼先讓咱們看一下代碼吧。以下apache

 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類)的工做以下。session

prepare成員變量的工做:app

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

2.建立一個新的action上下文。對於上下文不理解的讀者能夠查看相關的文章。若是不行的話,筆者認爲你能夠理解爲進入房間的門同樣子。action請求則是房間。新建一個房間就有一個新的門。新action請求就有一個新的action上下文。ide

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類的時候,講的比較多。就是要讓讀者明白準備工做都有哪些什麼。

相關文章
相關標籤/搜索