这张工作原理图是官方提供的:
一个请求在Struts2框架中的处理大概分为以下几个步骤
1、客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3、接着StrutsPrepareAndExecuteFilter(在struts2.1中就不用FilterDispatcher)被调用,StrutsPrepareAndExecuteFilter询问ActionMapper来决定这个请是否需要调用某个Action
4、如果ActionMapper决定需要调用某个Action,StrutsPrepareAndExecuteFilter把请求的处理交给ActionProxy
5、ActionProxy通过ConfigurationManager询问框架的配置文件,找到需要调用的Action类 ,这里,我们一般是从struts.xml配置中读取。
6、ActionProxy创建一个ActionInvocation的实例。
7、ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
在调用拦截器的时候是采取回掉模式;
StrutsPrepareAndExecuteFilter是控制器的核心,就是mvc中c控制层的核心。下面粗略的分析下我理解的StrutsPrepareAndExecuteFilter工作流程和原理:StrutsPrepareAndExecuteFilter进行初始化并启用核心doFilter
最近工作任务不是很紧,时间也不能白白浪费,以前常用的struts2框架源码没去了解过,所以就跟踪一下struts2的整个执行过程.由于本人也是抱着学习的态度来阅读掩码,若文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!希望能互相学习。
我这里的struts2源码是maven导jar包来查看源码的,这样方便多了,可以在IDE下查看源码。pom.xml文件如下
4.0.0
Struts_test_lishun
Struts_test_lishun
0.0.1-SNAPSHOT
war
commons-logging
commons-logging
1.2
org.apache.struts
struts2-core
2.3.24
action2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts.serve.static.browserCache
true
struts.action.extension
do
action2
/*
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();
}
}
public Dispatcher initDispatcher( HostConfig filterConfig ) {
//这里主要是对新建一个dispatcher,并加载配置文件中的初始化参数
Dispatcher dispatcher = createDispatcher(filterConfig);
//初始化dispatcher
dispatcher.init();
return dispatcher;
}
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);
}
}
———————-华丽的分割线————————-
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);
}
}
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;
}
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);
}
execute.executeAction(request, response, mapping);方法;
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
dispatcher.serviceAction(request, response, mapping);
}
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
throws ServletException {
//封转上下文环境,主要将request、params、session等Map封装成为一个上下文Map、
Map 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);
}
}
创建ActionProxy是由ActionProxyFactory实现类完成
DefaultActionProxyFactory createActionProxy方法
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map 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);
}
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;
}
}
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);
}
}
public void init(ActionProxy proxy) {
this.proxy = proxy;
Map 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 interceptorList = new ArrayList(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
}
至此action就创建成功了,然后就是给action的属性赋值和执行action里的方法
proxy.execute();方法
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);
}
}
.....其他代码省略
try {
//最主要执行了intercept方法,这里就是执行设置action的属性值和执行方法,由接口Interceptor的抽象类AbstractInterceptor的子类MethodFilterInterceptor执行intercept方法,
//由于AbstractInterceptor的实现类很多,所以这段代码会执行很多次,至于为什么会执行多次,本人也还在研究,若有仁兄了解可以给小弟一点指引
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
.....其他代码省略
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (applyInterceptor(invocation)) {
//执行了ParametersInterceptor的doIntercept方法,
return doIntercept(invocation);
}
return invocation.invoke();
}
@Override
public String doIntercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
if (!(action instanceof NoParameters)) {
ActionContext ac = invocation.getInvocationContext();
final Map parameters = retrieveParameters(ac);
if (LOG.isDebugEnabled()) {
LOG.debug("Setting params " + getParameterLogMap(parameters));
}
if (parameters != null) {
Map 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();
}
一个请求在Struts2框架中的处理大概分为以下几个步骤:
1 客户端发送请求;
2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action。FilterDispatcher的功能如下:
(1)执行Actions
(2)清除ActionContext
(3)维护静态内容
(4)清除request生命周期内的XWork的interceptors
4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5 ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6 ActionProxy创建一个ActionInvocation的实例。
7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper
拦截器与过滤器:
1、拦截器是基于java反射机制的,而过滤器是基于函数回调的。2、过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。
3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有请求起作用。
4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。
5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次。
在上述过程中所有的对象(Action,Results,Interceptors,等)都是通过ObjectFactory来创建的。
Struts2的目标很简单–使Web开发变得更加容易。为了达成这一目标,Struts2中提供了很多新特性,比如智能的默认设置、annotation的使用以及”惯例重于配置”原则的应用,而这一切都大大减少了XML配置。Struts2中的Action都是POJO,这一方面增强了Action本身的可测试性,另一方面也减小了框架内部的耦合度,而HTML表单中的输入项都被转换成了恰当的类型以供action使用。开发人员还可以通过拦截器(可以自定义拦截器或者使用Struts2提供的拦截器)来对请求进行预处理和后处理,这样一来,处理请求就变得更加模块化,从而进一步减小耦合度。模块化是一个通用的主题–可以通过插件机制来对框架进行扩展;开发人员可以使用自定义的实现来替换掉框架的关键类,从而获得框架本身所不具备的功能;可以用标签来渲染多种主题(包括自定义的主题);Action执行完毕以后,可以有多种结果类型–包括渲染JSP页面,Velocity和Freemarker模板,但并不仅限于这些。