您的位置:首页 > 编程语言 > Java开发

Struts2源码粗略分析三:请求处理流程分析

2010-10-31 17:57 821 查看

doFilter分析

当Web请求到达服务器时,请求就会被Servlet容器(Tomcat)派发到相应的Servlet中去,而调用这些Servlet之前,满足一定条件的Filter也会被调用,我们之前在web.xml中配置的StrutsPrepareAndExeuteFilter就属于满足条件的Filter,因为它会拦截所有URL。当Filter被Servlet容器调用时,doFilter方法就开始执行了。我们现在prepare.setEncodingAndLocale(request, response);这一行打一个断点,这样就可以观察到每一个步骤的细节问题。

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
} else {
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request, response, true);
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
execute.executeAction(request, response, mapping);
}
}
} finally {
prepare.cleanupRequest(request);
}
}


了解prepare

从字面上理解prepare.setEncodingAndLocale、prepare.createActionContext和prepare.assignDispatcherToThread的作用分别是用于处理编码与本地化、Action关联文信息、关联当前的Dispatcher与当前线程。

关于编码与本地化处理相关内容请参考源码与下图来理解:



方法createActionContext的作用就是创建一个与当前请求相关的ActionContext对象,后续的请求处理操作都会从这里取得相关信息。至于它到底把哪些东西进行了处理,是如何处理的,不在这里讲解,只说一下与ActionContext相关的技术问题。Struts1的一个缺陷就是Action是非线程安全的,也就是说,如果我们在Action中定义了共享资源,在并发请求时就可能会出现资源访问冲突的现象。举个例子来说,比如在Action中定义一个私有成员userName,这个userName每次都会从request中取得数据,当两个请求A和B先后到达后,B所得到的内容就会覆盖之前A所写入的内容,原因就是Action在内存中是唯一的,userName这个属性是共享!那么如何让两次请求可以互相独立呢,Struts2中的ActionContext就可以解决这个问题。

解读ActionContext

其实ActionContext并不是什么高级货,我们同样可以在Struts1中编写自己的ActionContext。下面我们详细解读一下ActionContext的源码。先看一下类图:



从类图上可以看出,ActionContext总共有两个部分,第一部分就是静态常量以及成员变量的定义,第二部分为操作方法定义。actionContext是一个精通的包级访问ThreadLocal类型变量,也就是说它仅对同包内的访问开放,拒绝其他访问。ThreadLocal在多线程访问中是一个很重要的概念,它将存储与每个线程管理起来,回顾一下上面A、B请求的例子,因为它们存放内容都在同一处,所以才会产生数据被覆盖的情况。而有了ThreadLocal之后,A可以把数据保存在与A线程相关的地方,B可以把数据保存在与B线程相关的地方,互不干扰。但为了能在一个地方储存更多的信息,Map结构可能就成为我们的首选数据结构了,所以类图中context就是一个Map类型的变量。存取数据时,最常用的就是get和put方法,而其他操作方法则是对Web程序的一种特定封装。(如果不了解ThreadLocal的话,先参考相关的JavaDoc,之后再来阅读ActionContext源码会收获更多)

如果真的明白了ActionContext的话,prepare.assignDispatcherToThread也就可以理解了,只是为当前Action要访问的Dispatcher找一个临时住所而已。

纯粹的Filter

excludedPatterns就是让StrutsPrepareAndExecuteFilter跳过某些URL不做后续的Struts处理,它的用法请参考Struts2相关文档说明。假设现在满足excludedPatterns的条件,那程序就会走入chain.doFilter中,也就是说,我们配置的Struts2实际上什么都没干,纯粹的一个Filter而已,没有使用到Struts2的任何功能。

资源清理

由于ActionContext和Dispatcher都使用到ThreadLocal,所以必须手动清理本次请求相关的内容,否则可能会因资源耗尽而产生内存溢出。具体的细节还是得参考ThreadLocal相关的文档。

请求处理 - StaticContentLoader VS ActionMapping

在正式进入处理之前,Struts2先对request对象进行包装:



下面为Dispatcher.wrapRequest方法实现。主要与文件上传有关,如果对文件上传原来与实现有兴趣,可以参考http://www.servlets.com/和http://www.servlets.com/cos/:

/**
* Wrap and return the given request or return the original request object.
* </p>
* This method transparently handles multipart data as a wrapped class around the given request.
* Override this method to handle multipart requests in a special way or to handle other types of requests.
* Note, {@link org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper} is
* flexible - look first to that object before overriding this method to handle multipart data.
*
* @param request the HttpServletRequest object.
* @param servletContext Our ServletContext object
* @return a wrapped request or original request.
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper
* @throws java.io.IOException on any error.
*/
public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {
// don't wrap more than once
if (request instanceof StrutsRequestWrapper) {
return request;
}
String content_type = request.getContentType();
if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {
MultiPartRequest mpr = null;
//check for alternate implementations of MultiPartRequest
Set<String> multiNames = getContainer().getInstanceNames(MultiPartRequest.class);
if (multiNames != null) {
for (String multiName : multiNames) {
if (multiName.equals(multipartHandlerName)) {
mpr = getContainer().getInstance(MultiPartRequest.class, multiName);
}
}
}
if (mpr == null ) {
mpr = getContainer().getInstance(MultiPartRequest.class);
}
request = new MultiPartRequestWrapper(mpr, request, getSaveDir(servletContext));
} else {
request = new StrutsRequestWrapper(request);
}
return request;
}


ActionMapping

接下来就要正式进入Struts2流程控制的核心了,这些核心组件会在ActionMapping所提供的信息下互相协作,完成请求处理。ActionMapping从哪里来呢?深入PrepareOperations.findActionMapping就会发现,它是从Dispatcher的Container中取出的,进一步分析Dispatcher的Container,我们就会发现,实现的核心内容是依赖于xwork的ConfigurationManager,这部分内容在下节会进行详细分析,这里只需要知道ActionMapping是根据xwork的ConfigurationManager读取struts.xml返回的就足够了。ActionMapping返回后,处理就被分为两部分,根据当前所请求的URL,如果ActionMapping找到时,请求就被转发至Action处理,否则会根据静态配置信息来尝试取得静态资源。

静态资源处理

当以下面形式配置静态内容时,demo里所存放的内容就会被直接访问,这个打破了WEB-INF中内容不允许外部访问的常规。访问URL为:http://your_site:port/static/demo/your_static_resource

<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name>packages</param-name>
<param-value>demo</param-value>
</init-param>
</filter>


Action处理

当ActionMapping被成功的访问且不为空时,Action的处理就正式开始了。调用情况如下,基本上是利用xwork的功能,我们在下节,详细解读一下xwork的工作原理。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: