Struts2.0简介
Struts2是阿帕奇旗下的一款流行的Web框架,基于MVC模式,成功结合了Webwok和Struts1.X的优势。虽然名字上叫做Struts2.0,但除此之外它和Struts1.X的关系并不大。
Struts2.0的核心功能都是使用拦截器实现“值栈”这个概念,OGNL表达式和Struts2.0标签来解决应用程序数据,以及大量的注解,使得这个框架更易于使用。这些优势足以说服我们使用Struts2.0作为Web开发的框架。
Struts2.0的请求流程
- 客户端发送一个请求到Servlet容器,也就是Tomcat之类的服务器;
- 请求经过一系列过滤器(其中有一个
ActionContextCleanUp
的过滤器,该过滤器可选,它的功能是在调用完doFilter()方法之后,决定cleanUp哪些ActionContext
,以及,哪些不清空,以保证后续的filter不被清空,延长Action中属性的寿命); - 接着调用核心过滤器(相当于前端控制器的角色,让我想起Spring中的DispatcherServlet),也就是
StrutsPrepareAndExcuteFilter
图中是FilterDispatcher,FilterDispatcher在早期的版本中使用的较多,在2.1.3之后就不再推荐使用,它会询问ActionMapper
是否调用某个Action
; - 如果
ActionMapper
决定调用某个Action
返回对应Action的mapping信息给StrutsPrepareAndExcuteFilter,StrutsPrepareAndExcuteFilter
再将mapping交给ActionProxy
,由ActionProxy
来通过ConfigurationManager询问配置文件,找到指定的Action,也就是上一篇中的struts.xml
; ActionProxy
创建一个ActionInvocation
实例,ActionInvocation实例使用命名模式来调用(这里的命名模式十分疑惑,经查证源码后发现,应当是从xml文件中读取Action的名称,然后用java反射的方式创建实例),在Action
调用前后,涉及Interceptor
(拦截器)相关调用。- Action执行结束后,
ActionInvocation
负责根据struts.xml
中的配置找到返回结果。返回结果通常是一个需要被表示的jsp或者FreeMarker
模板(但并不总是,也可能是另一个Action链)。
参考流程
//TO BE CONTINUE
我们有必要来研究一下源码:
顺序上按照请求处理的顺序,首先,我们查看StrutsPrepareAndExcuteFilter
的源码,其doFilter()方法如下:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//获得请求
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
try {
String uri = RequestUtils.getUri(request);
//在这里判断请求是否交给Struts2处理
if (this.excludedPatterns != null && this.prepare.isUrlExcluded(request, this.excludedPatterns)) {
LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
chain.doFilter(request, response);
//是交由Struts2处理的请求
} else {
LOG.trace("Checking if {} is a static resource", uri);
boolean handled = this.execute.executeStaticResourceRequest(request, response);
if (!handled) {
LOG.trace("Assuming uri {} as a normal action", uri);
this.prepare.setEncodingAndLocale(request, response);
//创建ActionContext
/*ValueStack stack = //((ValueStackFactory)this.dispatcher.getContainer().getInstance(ValueStackFactory.class)).createValueStack();
stack.getContext().putAll(this.dispatcher.createContextMap(request, response, (ActionMapping)null));
ctx = new ActionContext(stack.getContext());
*/
//以上是PrepareOperation的createActionContext()方法的核心代码,这里创建值栈,并且将请求参数等内容放入了值栈的Context部分,再用该值栈创建开一个新的ActionContext。
this.prepare.createActionContext(request, response);
this.prepare.assignDispatcherToThread();
//包装原生request对象,在此之前,它是apche包装的request对象,而之后则转换为Struts2的包装对象。
request = this.prepare.wrapRequest(request);
//将请求交给ActionMapping进行分析
ActionMapping mapping = this.prepare.findActionMapping(request, response, true);
//判断mapping是否为空
if (mapping == null) {
LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
chain.doFilter(request, response);
} else {
LOG.trace("Found mapping {} for {}", mapping, uri);
this.execute.executeAction(request, response, mapping);
}
}
}
} finally {
this.prepare.cleanupRequest(request);
}
- 获得请求;
- 在这里判断请求是否交给Struts2处理;
- 如果不交由Struts2处理,则继续交给拦截器中链处理,否则创建一个ActionContext。ActionContext由PrepareOperation的createActionContext()方法创建,方法内创建值栈,并且将请求参数等内容放入了值栈的Context部分,再用该值栈创建开一个新的ActionContext;
- 将原生request对象进行包装,将getAttribute()重新包装,包装之后的getAttribute()方法将先从原生域中查找对象,如果找不到,再从ValueStack的栈中查找,最后会在ValueStack中的Context中查找,这个过程在StrutsRequestWrapper的源码中可以看到;
- 使用ActionMapping从请求中抽取信息,调用PrepareOperation对象prepare的findActionMapping来查找actionMapping,该方法内部使用了ActionMapper来查找Action的配置信息,判断mapping是否为空,是则将其交由后续filterChain处理,否则执行调用excuteOperation对象执行excuteAction方法;
- 执行excuteAction方法,该方法是中间方法,它的内部执行了Dispatcher对象的serviceAction方法,根据mapping中包含的信息创建ActionProxy对象,调用proxy对象的excute方法,该方法内部使用了ActionInvocation的invoke方法,excuteAction方法代码如下:
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
Map<String, Object> extraContext = this.createContextMap(request, response, mapping);
ValueStack stack = (ValueStack)request.getAttribute("struts.valueStack");
boolean nullStack = stack == null;
if (nullStack) {
ActionContext ctx = ActionContext.getContext();
if (ctx != null) {
stack = ctx.getValueStack();
}
}
if (stack != null) {
extraContext.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", this.valueStackFactory.createValueStack(stack));
}
String timerKey = "Handling request from Dispatcher";
try {
UtilTimerStack.push(timerKey);
//从mapping中抽取所需属性信息
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
//创建ActionProxy对象
ActionProxy proxy = ((ActionProxyFactory)this.getContainer().getInstance(ActionProxyFactory.class)).createActionProxy(namespace, name, method, extraContext, true, false);
request.setAttribute("struts.valueStack", proxy.getInvocation().getStack());
if (mapping.getResult() != null) {
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
//该方法内部调用了invocation.invoke()方法,这个invocation是DefaultActionInvocation类型,其内部invoke()方法会使用集合类的hasNext()将当前invocation传入所需的拦截器链的intercept方法,当interceptor的所有intercept执行完毕后,调用invocation的invokeActionOnly方法,执行action。
proxy.execute();
}
if (!nullStack) {
request.setAttribute("struts.valueStack", stack);
}
} catch (ConfigurationException var17) {
this.logConfigurationException(request, var17);
this.sendError(request, response, 404, var17);
} catch (Exception var18) {
if (!this.handleException && !this.devMode) {
throw new ServletException(var18);
}
this.sendError(request, response, 500, var18);
} finally {
UtilTimerStack.pop(timerKey);
}
}
7.执行Action。获得执行结果resultCode,也就是返回值”success”,接着判断结果是否执行,否则调用excuteResult(),然后就会调用各类result。
本文参考