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类的时候,讲的比较多。就是要让读者明白准备工做都有哪些什么。