首页 > 代码库 > Struts2 源码分析——过滤器(Filter)
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类现实于接口StrutsStatics。这接口都是大量常量。而StrutsPrepareFilter类本身有一个常量二个成员变量。其中PrepareOperations类的工作如下。
PrepareOperations类:
1.设置request请求的本地化。即是:本地语言和编码格式。
2.创建一个新的action上下文。对于上下文不理解的读者可以查看相关的文章。如果不行的话,笔者认为你可以理解为进入房间的门一样子。action请求则是房间。新建一个房间就有一个新的门。新action请求就有一个新的action上下文。
3.把当前的Dispatcher实例存放到当前的本地线程(ThreadLocal)中。而Dispatcher类是一个重要的核心类,struts2的action请求就是靠他来执行的。(对于Dispatcher类的作用不理解的话。没有事。后面我会对Dispatcher进行讲解)
4.把HttpServletRequest请求封装成为StrutsRequestWrapper或是MultiPartRequestWrapper。可以说这部分的工作也是靠Dispatcher实例来执行的。
5.找到ActionMapping类的实例。并把他存在到request请求里面。他对应的Key为“struts.actionMapping”; 读者会问ActionMapping类是什么东东。现在可以理解为里面存放用户action的配置信息。大白话就是用户在地址栏上输入URL找到对应的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请求执行之前做好一切准备的类。
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 }
笔者在这个类上的注解比较简单,主要是笔者不知道这个类什么样子去讲。因为这个类比StrutsPrepareFilter类来讲简单多了。工作也很单一。所以笔者一下子不知道要什么样子去注解。笔者认为这个类的重点有二个地方。一是lazyInit方法,二是ExecuteOperations类的工作。而其中lazyInit方法主要是用于初始化相关需要的类。并没有值得注意的点。那么为什么笔者却要说他是重点之一呢?问题在于他还有一个工作是初始化静态内容加载器(StaticContentLoader类)。可惜不是本章的重点。所以笔者要讲的是ExecuteOperations类的工作。如下
ExecuteOperations类的工作:
1.组装相关的Map类。如requestMap,params,session 等。
2.找到ActionProxy类。该类是用于执行action请求的。也是关键的类。(后面章节会讲到)
3.组装action请求执行的结果。也是关键的类。(后面章节会讲到)
StrutsExecuteFilter类的工作目前只需要知道他是执行action请求的。如果读者不明白不要担心。笔者后面会讲到。
本章总结 |
本章的重点并不是要知道如何去执行action请求。而是知道在执行action请求之前要做些什么工作。只要明白了这一点我们就知道目标是什么。所以在本章笔者在讲StrutsPrepareFilter类的时候,讲的比较多。就是要让读者明白准备工作都有哪些什么。
Struts2 源码分析——过滤器(Filter)