struts请求源码的跟踪
最近工作任务不是很紧,时间也不能白白浪费,以前常用的struts2框架源码没去了解过,所以就跟踪一下struts2的整个执行过程.由于本人也是抱着学习的态度来阅读掩码,若文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!希望能互相学习。
我这里的struts2源码是maven导jar包来查看源码的,这样方便多了,可以在IDE下查看源码。pom.xml文件如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Struts_test_lishun</groupId>
<artifactId>Struts_test_lishun</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.24</version>
</dependency>
</dependencies>
</project>
1.实现从web.xml说起:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<!-- 添加一个过滤器-->
<filter>
<filter-name>action2</filter-name>
<!-- struts2处理过程的入口 -->
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name >struts.serve.static.browserCache</param-name>
<param-value>true</param-value>
</init-param>
<!-- 初始化参数(访问后缀为.do),这里的param-name都可以从后面提到的源码中查看到,具体的其他功能本人也不是全部了解,
有兴趣的可以自己再去深入,-->
<init-param>
<param-name>struts.action.extension</param-name>
<param-value>do</param-value>
</init-param>
</filter> <filter-mapping>
<filter-name>action2</filter-name>
<!-- 过滤所有的请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
2:开始查看StrutsPrepareAndExecuteFilter 过滤器
2-1:初始化:(tomcat一启动就会执行到这里)
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
Dispatcher dispatcher = null;
try {
//封装filterConfig,便于对xml文件的操作
FilterHostConfig config = new FilterHostConfig(filterConfig);
//初始化日志
init.initLogging(config);
//初始化dispatcher,struts2的核心类,大部分的操作都是在这里完成的
dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
//初始化当前类属性:prepare 、execute(这两个属性会在doFilter里设置ActionContext和encoding的值)
prepare = new PrepareOperations(dispatcher);
execute = new ExecuteOperations(dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//这是是空函数。说是回调方法,这里不太懂,有了解可以给小弟指引下
postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
2-2:对上面init.initDispatcher(config)的跟踪:
public Dispatcher initDispatcher( HostConfig filterConfig ) {
//这里主要是对新建一个dispatcher,并加载配置文件中的初始化参数
Dispatcher dispatcher = createDispatcher(filterConfig);
//初始化dispatcher
dispatcher.init();
return dispatcher;
}
2-2-1:dispatcher.init()跟踪:
public void init() { if (configurationManager == null) {
//把struts.xml文件进行封装(默认是名字是struts)
configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
} try { init_FileManager();
//加载org/apache/struts2/default.properties
init_DefaultProperties(); // [1]
//加载struts-default.xml,struts-plugin.xml,struts.xml
init_TraditionalXmlConfigurations(); // [2]
//下面的初始化代码。没去研究,到此已经把web.xml,和struts.xml文件给加载进来了
init_LegacyStrutsProperties(); // [3]
init_CustomConfigurationProviders(); // [5]
init_FilterInitParameters() ; // [6]
init_AliasStandardObjects() ; // [7] Container container = init_PreloadConfiguration();
//注入dispatcher
container.inject(this);
init_CheckWebLogicWorkaround(container); if (!dispatcherListeners.isEmpty()) {
for (DispatcherListener l : dispatcherListeners) {
l.dispatcherInitialized(this);
}
}
errorHandler.init(servletContext); } catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}
初始化就在此告一段落了。
----------------------华丽的分割线-------------------------
3-1:访问某个action,StrutsPrepareAndExecuteFilter对所有的的请求都会过滤,此事就跟踪到;
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)) {
chain.doFilter(request, response);
} else {
//设置类属性编码和国际化
prepare.setEncodingAndLocale(request, response);
//设置类属性action的上下文
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
//对request进行封装,继续跟踪下去代码会发现这里的作用就是根据不同的请求返回不同request的封装类(这里就是用到装饰者模式)
request = prepare.wrapRequest(request);
//返回ActionMapping:里面有通过struts.xml文件通过反射获取到action对应的类和方法
ActionMapping mapping = prepare.findActionMapping(request, response, true);
//若返回的ActionMapping为null,则表示不是调用action
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else { //这里进行了很多操作,比较重要就是页面参数值的注入,和执行action
execute.executeAction(request, response, mapping);
}
}
} finally {
prepare.cleanupRequest(request);
}
}
3-2:追踪代码-生成映射action的映射:
ActionMapping mapping = prepare.findActionMapping(request, response, true);
在PrepareOperations中,主要执行ActionMapper的getMapping(..)方法
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
if (mapping == null || forceLookup) {
try {
//new一个ActionMapper实例并执行getMapping(..)方法
mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
if (mapping != null) {
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
}
} catch (Exception ex) {
dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
}
} return mapping;
}
3-2-1:追踪到ActionMapper的getMapping(..)方法;而ActionMapper是一个接口,方法由DefaultActionMapper类实现;
定位到DefaultActionMapper类的getMapping(..);
public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
ActionMapping mapping = new ActionMapping();
//获取到请求的url
String uri = RequestUtils.getUri(request);
//截取url,把含有";"的后面的字符截取掉
int indexOfSemicolon = uri.indexOf(";");
uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri; //继续截取url,url的后缀截取掉(如.action和.do等),在这里就可以看见extensions变量,struts默认的后缀名就是.action
uri = dropExtension(uri, mapping);
if (uri == null) {
return null;
}
//从url获取namespace和name并和mapping匹配
parseNameAndNamespace(uri, mapping, configManager);
handleSpecialParameters(request, mapping);
return parseActionName(mapping);
}
3-3:现在返回到doFilter方法,追踪到
execute.executeAction(request, response, mapping);方法;
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
dispatcher.serviceAction(request, response, mapping);
}
最终调用的是Dispatcher的serviceAction(...)方法,
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
throws ServletException {
//封转上下文环境,主要将request、params、session等Map封装成为一个上下文Map、
Map<String, Object> extraContext = createContextMap(request, response, mapping); // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
boolean nullStack = stack == null;
if (nullStack) {
ActionContext ctx = ActionContext.getContext();
if (ctx != null) {
stack = ctx.getValueStack();
}
}
if (stack != null) {
extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
} String timerKey = "Handling request from Dispatcher";
try {
UtilTimerStack.push(timerKey);
//从mapping(这里是已经封装好的mapping,struts.xml与请求url共同映射出来的数据)中获取命名空间
String namespace = mapping.getNamespace();
//从mapping中获取action的name
String name = mapping.getName();
//从mapping中获取请求方法(是获取动态的请求方法:在url后面加上 ‘!+方法名’)
String method = mapping.getMethod();
//生成action的代理类,执行页面参数值和根据反射执行请求方法
ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); //如果配置文件中执行的这个action配置了result,就直接转到result
if (mapping.getResult() != null) {
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
proxy.execute();
} // If there was a previous value stack then set it back onto the request
if (!nullStack) {
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch (ConfigurationException e) {
logConfigurationException(request, e);
sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
} catch (Exception e) {
if (handleException || devMode) {
sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
} else {
throw new ServletException(e);
}
} finally {
UtilTimerStack.pop(timerKey);
}
}
3-3-1:追踪 ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
创建ActionProxy是由ActionProxyFactory实现类完成
DefaultActionProxyFactory createActionProxy方法
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) { ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
//StrutsActionProxyFactory 的createActionProxy方法, StrutsActionProxyFactory是DefaultActionProxyFactory的子类,调用的是StrutsActionProxyFactory的方法
return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
}
--------- StrutsActionProxyFactory-----------------
public class StrutsActionProxyFactory extends DefaultActionProxyFactory { @Override
public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) { StrutsActionProxy proxy = new StrutsActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
container.inject(proxy);
//继续跟踪代码: prepare()调用的是父类DefaultActionProxy的prepare()方法
proxy.prepare();
return proxy;
}
}
--------------------DefaultActionProxy-------------------
protected void prepare() {
String profileKey = "create DefaultActionProxy: ";
try {
UtilTimerStack.push(profileKey);
config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName); if (config == null && unknownHandlerManager.hasUnknownHandlers()) {
config = unknownHandlerManager.handleUnknownAction(namespace, actionName);
}
if (config == null) {
throw new ConfigurationException(getErrorMessage());
}
//获取执行method为空的方法名, 若为空则默认设置为"execute"
resolveMethod(); if (!config.isAllowedMethod(method)) {
throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);
}
//创建action
invocation.init(this); } finally {
UtilTimerStack.pop(profileKey);
}
}
----------------- invocation.init(this)--------------
public void init(ActionProxy proxy) {
this.proxy = proxy;
Map<String, Object> contextMap = createContextMap(); // Setting this so that other classes, like object factories, can use the ActionProxy and other
// contextual information to operate
ActionContext actionContext = ActionContext.getContext(); if (actionContext != null) {
actionContext.setActionInvocation(this);
}
//这里开始创建action
createAction(contextMap); if (pushAction) {
stack.push(action);
contextMap.put("action", action);
} invocationContext = new ActionContext(contextMap);
invocationContext.setName(proxy.getActionName()); // get a new List so we don't get problems with the iterator if someone changes the list
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
}
至此action就创建成功了,然后就是给action的属性赋值和执行action里的方法
3-4:重新回到Dispatcher的serviceAction(...)方法,当mapping.getResult() != null此时就跟踪到
proxy.execute();方法
ActionProxy是接口,execute()由StrutsActionProxy实现;
public String execute() throws Exception {
ActionContext previous = ActionContext.getContext();
ActionContext.setContext(invocation.getInvocationContext());
try {
//开始执行action和注入属性值,由DefaultActionInvocation实现这个方法
return invocation.invoke();
} finally {
if (cleanupContext)
ActionContext.setContext(previous);
}
}
---------------DefaultActionInvocation--------------------------------
.....其他代码省略
try {
//最主要执行了intercept方法,这里就是执行设置action的属性值和执行方法,由接口Interceptor的抽象类AbstractInterceptor的子类MethodFilterInterceptor执行intercept方法,
//由于AbstractInterceptor的实现类很多,所以这段代码会执行很多次,至于为什么会执行多次,本人也还在研究,若有仁兄了解可以给小弟一点指引
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
.....其他代码省略
---------------MethodFilterInterceptor---------------------------------
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (applyInterceptor(invocation)) {
//执行了ParametersInterceptor的doIntercept方法,
return doIntercept(invocation);
}
return invocation.invoke();
}
---------------ParametersInterceptor---------------------
@Override
public String doIntercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
if (!(action instanceof NoParameters)) {
ActionContext ac = invocation.getInvocationContext();
final Map<String, Object> parameters = retrieveParameters(ac); if (LOG.isDebugEnabled()) {
LOG.debug("Setting params " + getParameterLogMap(parameters));
} if (parameters != null) {
Map<String, Object> contextMap = ac.getContextMap();
try {
ReflectionContextState.setCreatingNullObjects(contextMap, true);
ReflectionContextState.setDenyMethodExecution(contextMap, true);
ReflectionContextState.setReportingConversionErrors(contextMap, true); ValueStack stack = ac.getValueStack();
//给action的参数赋值
setParameters(action, stack, parameters);
} finally {
ReflectionContextState.setCreatingNullObjects(contextMap, false);
ReflectionContextState.setDenyMethodExecution(contextMap, false);
ReflectionContextState.setReportingConversionErrors(contextMap, false);
}
}
}
//最后执行action
return invocation.invoke();
}
至此,整个action的请求就结束了;


struts请求源码的跟踪的更多相关文章
- # Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析#
Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析 Volley源码一共40多个类和接口.除去一些工具类的实现,核心代码只有20多个类.所以相对来说分析起来没有那么吃力.但是要想分析透 ...
- 【图解源码】Zookeeper3.7源码分析,包含服务启动流程源码、网络通信源码、RequestProcessor处理请求源码
Zookeeper3.7源码剖析 能力目标 能基于Maven导入最新版Zookeeper源码 能说出Zookeeper单机启动流程 理解Zookeeper默认通信中4个线程的作用 掌握Zookeepe ...
- Tomcat处理HTTP请求源码分析(下)
转载:http://www.infoq.com/cn/articles/zh-tomcat-http-request-2 很多开源应用服务器都是集成tomcat作为web container的,而且对 ...
- Tomcat处理HTTP请求源码分析(上)
Tomcat处理HTTP请求源码分析(上) 作者 张华 发布于 2011年12月8日 | 8 讨论 分享到: 微博 微信 Facebook Twitter 有道云笔记 邮件分享 稍后阅读 我的阅读清单 ...
- spark源码单步跟踪阅读-从毛片说起
想当年读大学时,那时毛片还叫毛片,现在有文明的叫法了,叫小电影或者爱情动作片.那时宿舍有位大神,喜欢看各种毛片,当我们纠结于毛片上的马赛克时,大神大手一挥说道:这算啥,阅尽天下毛片,心中自然无码!突然 ...
- Tomcat处理HTTP请求源码分析(上)(转)
转载自:http://www.infoq.com/cn/articles/zh-tomcat-http-request-1 很多开源应用服务器都是集成tomcat作为web container的,而且 ...
- 知识小罐头07(tomcat8请求源码分析 下)
感觉最近想偷懒了,哎,强迫自己也要写点东西,偷懒可是会上瘾的,嘿嘿!一有写博客的想法要赶紧行动起来,养成良好的习惯. ok,继续上一篇所说的一些东西,上一篇说到Connector包装了那两个对象,最后 ...
- 知识小罐头06(tomcat8请求源码分析 中)
更正上一篇一个小错误,Connector中首先是将socket请求过来的信息封装成一个普通的Request对象(上一篇我写成HttpRequest对象,失误失误,根本就木有HttpRequest这样的 ...
- 知识小罐头05(tomcat8请求源码分析 上)
这一篇我们不看源码,就大概理一下Tomcat内部组成部分!前面花费了两篇博客的篇幅来说说了一般的maven web项目并部署到tomcat运行,其实都是为这篇做铺垫的! 其实我下载了tomcat7,t ...
随机推荐
- Gym 100917J---dir -C(RMQ--ST)
题目链接 http://codeforces.com/gym/100917/problem/D problem description Famous Berland coder and IT mana ...
- php中的常用数组函数(七) 数组合并 array_merge()和array_merge_recursive()
$arr1 = array(1, 2, 3, 4, 'color'=>'red'); $arr2 = array('a', 'b', 'c', 'color'=>'blue'); prin ...
- 习题:codevs 2822 爱在心中 解题报告
这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...
- 指针,&的用法
#include "stdafx.h" #include <stdio.h> #include <string.h> int main() { ] = {, ...
- CGI与Servlet的比较
转自:http://www.maxhis.info/java/cgi-vs-servlet/ 谢! 概括来说,CGI和Servlet可以完成相同的功能. 一:CGI(Common Gateway In ...
- GJM :C++ 网络编程 [转载]
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- 基于软件开源实践(FLOSS)论共产主义的可实现性
好久没发博客,来个狠的,我不信挨踢界有人比我更蛋疼来研究这个. 在马克思提出共产主义100多百年后,软件开发领域中出现了一种特别的生产方式:开源(FLOSS:Free/Libre and Open S ...
- css居中完全指南(翻译)
最近参加了百度的前端技术学院,任务4是要求一个元素在父元素中水平和垂直居中,提供的一篇文章对各种情况都进行了分析,很不错,英文也不是那么难懂,毕竟代码还是主体,翻译过来分享出来,翻译内容带有自己的理解 ...
- [deviceone开发]-企业OA项目开源分享
一.简介 是一个真实的企业OA项目改造的开源项目,几乎涵盖了所有常用的组件,包括环信实现在线聊天等功能,类似微信的朋友圈功能,自定义的智能搜索等,而且这个是真实的通过Http链接后台web服务,里面很 ...
- Location对象、History对象
Location对象: Window对象的location属性引用的是Location对象,它表示窗口中当前显示的内容的URL,Document对象的location属性也引用Location对象,w ...