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运行起来的时候,框架也有相关的提示你。那么先让我们看一下代码吧。如下
package org.apache.struts2.dispatcher.filter; import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.InitOperations;
import org.apache.struts2.dispatcher.PrepareOperations; import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern; public class StrutsPrepareFilter implements StrutsStatics, Filter { protected static final String REQUEST_EXCLUDED_FROM_ACTION_MAPPING = StrutsPrepareFilter.class.getName() + ".REQUEST_EXCLUDED_FROM_ACTION_MAPPING"; protected PrepareOperations prepare;//用于每一次请求之前,执行一些功能的类。
protected List<Pattern> excludedPatterns = null; public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();//用于初始化相关的功能操作。你可以理解为工具类一样子。
Dispatcher dispatcher = null;//这个类相当的重要。他的作用连接着StrutsExecuteFilter。这里可以命名为调结者。
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);//这里可以理解为把filterConfig在进行封装FilterHostConfig更为主便操作和理解。
init.initLogging(config);//获取名为loggerFactory的参数,并实例化这个类。一般为去用户自定义日志。
dispatcher = init.initDispatcher(config);//初化调结者。这里是重要。 prepare = new PrepareOperations(dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);//加载排除在内的action的正则表达式 postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
} protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
} public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res; try {
if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
request.setAttribute(REQUEST_EXCLUDED_FROM_ACTION_MAPPING, new Object());
} else {
prepare.setEncodingAndLocale(request, response);//设置请求的格式编码。
prepare.createActionContext(request, response);//action的上下文
prepare.assignDispatcherToThread();//把Dispatcher放入本地线程里面。
request = prepare.wrapRequest(request);
prepare.findActionMapping(request, response);//找到action映射的信息
}
chain.doFilter(request, response);
} finally {
prepare.cleanupRequest(request);
}
} public void destroy() {
prepare.cleanupDispatcher();
} }
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请求。让我们先看一下代码吧。如下
package org.apache.struts2.dispatcher.filter; import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.ExecuteOperations;
import org.apache.struts2.dispatcher.InitOperations;
import org.apache.struts2.dispatcher.PrepareOperations; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; public class StrutsExecuteFilter implements StrutsStatics, Filter {
protected PrepareOperations prepare;//用于每一次请求之前,执行一些功能的类。
protected ExecuteOperations execute;//用于执行请求的功能类。 protected FilterConfig filterConfig; public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
} protected synchronized void lazyInit() {
if (execute == null) {
InitOperations init = new InitOperations();//用于初始化的功能类
Dispatcher dispatcher = init.findDispatcherOnThread();
init.initStaticContentLoader(new FilterHostConfig(filterConfig), dispatcher); prepare = new PrepareOperations(dispatcher);
execute = new ExecuteOperations(dispatcher);
} } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res; if (excludeUrl(request)) {//用于判断是否在排除的action之内。如果是就跳过。
chain.doFilter(request, response);
return;
} if (execute == null) {
lazyInit();//初始化相关的信息类。
} ActionMapping mapping = prepare.findActionMapping(request, response);//找到ActionMapping实例 Integer recursionCounter = (Integer) request.getAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER); if (mapping == null || recursionCounter > 1) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
execute.executeAction(request, response, mapping);//执行action请求
}
} private boolean excludeUrl(HttpServletRequest request) {
return request.getAttribute(StrutsPrepareFilter.REQUEST_EXCLUDED_FROM_ACTION_MAPPING) != null;
} public void destroy() {
prepare = null;
execute = null;
filterConfig = null;
} }
StrutsExecuteFilter类的源码
笔者在这个类上的注解比较简单,主要是笔者不知道这个类什么样子去讲。因为这个类比StrutsPrepareFilter类来讲简单多了。工作也很单一。所以笔者一下子不知道要什么样子去注解。笔者认为这个类的重点有二个地方。一是lazyInit方法,二是ExecuteOperations类的工作。而其中lazyInit方法主要是用于初始化相关需要的类。并没有值得注意的点。那么为什么笔者却要说他是重点之一呢?问题在于他还有一个工作是初始化静态内容加载器(StaticContentLoader类)。可惜不是本章的重点。所以笔者要讲的是ExecuteOperations类的工作。如下
ExecuteOperations类的工作:
1.组装相关的Map类。如requestMap,params,session 等。
2.找到ActionProxy类。该类是用于执行action请求的。也是关键的类。(后面章节会讲到)
3.组装action请求执行的结果。也是关键的类。(后面章节会讲到)
StrutsExecuteFilter类的工作目前只需要知道他是执行action请求的。如果读者不明白不要担心。笔者后面会讲到。
本章总结 |
本章的重点并不是要知道如何去执行action请求。而是知道在执行action请求之前要做些什么工作。只要明白了这一点我们就知道目标是什么。所以在本章笔者在讲StrutsPrepareFilter类的时候,讲的比较多。就是要让读者明白准备工作都有哪些什么。
Struts2 源码分析——过滤器(Filter)的更多相关文章
- Struts2 源码分析——调结者(Dispatcher)之执行action
章节简言 上一章笔者写关于Dispatcher类如何处理接受来的request请求.当然读者们也知道他并非正真的执行action操作.他只是在执行action操作之前的准备工作.那么谁才是正真的执行a ...
- Struts2 源码分析——Hello world
新建第一个应用程序 上一章我们讲到了关于struts2核心机制.对于程序员来讲比较概念的一章.而本章笔者将会亲手写一个Hello world的例子.所以如果对struts2使用比较了解的朋友,请跳过本 ...
- Struts2 源码分析——DefaultActionInvocation类的执行action
本章简言 上一章讲到关于拦截器的机制的知识点,让我们对拦截器有了一定的认识.我们也清楚的知道在执行用户action类实例之前,struts2会先去执行当前action类对应的拦截器.而关于在哪里执行a ...
- Struts2 源码分析——拦截器的机制
本章简言 上一章讲到关于action代理类的工作.即是如何去找对应的action配置信息,并执行action类的实例.而这一章笔者将讲到在执行action需要用到的拦截器.为什么要讲拦截器呢?可以这样 ...
- Struts2 源码分析——Action代理类的工作
章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息.相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识.而本章将讲到关于struts2启动成功 ...
- Struts2 源码分析——配置管理之PackageProvider接口
本章简言 上一章讲到关于ContainerProvider的知识.让我们知道struts2是如何注册相关的数据.也知道如何加载相关的配置信息.本章笔者将讲到如何加载配置文件里面的package元素节点 ...
- Struts2 源码分析——配置管理之ContainerProvider接口
本章简言 上一章笔者讲到关于Dispatcher类的执行action功能,知道了关于执行action需要用到的信息.而本章将会讲到的内容也跟Dispatcher类有关系.那就是配置管理中的Contai ...
- Struts2 源码分析——项目分析
项目知识点分析 从上一章中我们知道了接下来我们要去了解源码的项目(struts2-showcase).而这一章将讲述我三年后在次接触struts2-showcase项目是一个什么样子的情况.我有一个工 ...
- Struts2 源码分析-----工作原理分析
请求过程 struts2 架构图如下图所示: 依照上图,我们可以看出一个请求在struts的处理大概有如下步骤: 1.客户端初始化一个指向Servlet容器(例如Tomcat)的请求: 2.这个请求经 ...
随机推荐
- [ASE][Daily Scrum]12.05
占坑 最近大家都很忙所以工作准备放到周末来做……所以这两天进度会比较慢.
- 快速学习C语言三: 开发环境, VIM配置, TCP基础,Linux开发基础,Socket开发基础
上次学了一些C开发相关的工具,这次再配置一下VIM,让开发过程更爽一些. 另外再学一些linux下网络开发的基础,好多人学C也是为了做网络开发. 开发环境 首先得有个Linux环境,有时候家里机器是W ...
- Razor 中的@helper 与 @function 用法
@helper : 可以有返回值,也可以没有返回值 @function :需要有返回值 可以将View中公共部分的代码抽取出来,变成一个独立的方法 公共部分 view 抽出的公共部分 ...
- PyCharm 3.0 发布,提供免费开源版本
PyCharm 发布最新的 3.0 版本,该版本新特性详见: http://www.jetbrains.com/pycharm/whatsnew/index.html 该版本最主要的是提供了免费开源的 ...
- Vs2013 头文件注释
在vs2013的默认安装目录 1.CS类修改方式 在C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ItemTempla ...
- 依赖注入的威力,.NET Core的魅力:解决MVC视图中的中文被html编码的问题
有园友在博问中提了这样一个问题 —— .NET Core 中文等非英文文字html编码输出问题,到我们的 ASP.NET Core 项目中一看,也是同样的问题. 比如下面的Razor视图代码: @{ ...
- js实现快速排序
非原创: var quickSort = function(arr){ if(arr.length<=1){return arr;} var pivotIndex = Math.floor(ar ...
- How to fix updating ubuntu apt-get problem
It's my new PC with a new os of ubuntu. every time when I want to install software or update apt-get ...
- python:how does subclass call baseclass's __init__()
First, use baseclass's name to call __init__() I wrote code like this: and we can use 'super' too.
- java内功 ---- jvm虚拟机原理总结,侧重于虚拟机类加载执行系统
参考书籍:<深入理解java虚拟机>,三天时间用了八个小时看完,像读一本武侠小说,挺爽. 另外需声明:图片都是从我自己的csdn博客转载,所以虽然有csdn标识,但都是我自己画的图片. j ...