这个过程是从客户端发起访问,到服务端响应,并返回结果的整个过程。
以struts-2.5.5作为研究对象
第一部分 从StrutsPrepareAndExecuteFilter开始
StrutsPrepareAndExecuteFilter作为struts2的一个核心过滤器(Filter),接受所有客户端的请求,经过一系列的处理才到我们的Action。对于Filter,他首先执行init方法初始化,因此我们先看看StrutsPrepareAndExecuteFilter的初始化做了什么。
InitOperations
Dispatcher
Container
ActionMapping
ActionProxy
Interceptor
StrutsPrepareAndExecuteFilter.init
public void init(FilterConfig filterConfig) throws ServletException {
//初始化InitOperations
InitOperations init = new InitOperations();
Dispatcher dispatcher = null;
try {
//获取web.xml的Filter内的init-param参数配置
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
//当前请求运行之前的包含初始化的操作--仅产生PrepareOperations对象
prepare = new PrepareOperations(dispatcher);
//所有过滤器所包含运行操作--仅产生ExecuteOperations对象
execute = new ExecuteOperations(dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
接着我们看具体dispatcher 的创建过程,createDispatcher方法。
InitOperations.initDispatcher
public Dispatcher initDispatcher( HostConfig filterConfig ) {
**Dispatcher** dispatcher = createDispatcher(filterConfig);
dispatcher.init();
return dispatcher;
}
InitOperations.createDispatcher
private Dispatcher createDispatcher( HostConfig filterConfig ) {
Map<String, String> params = new HashMap<>();
//遍历filterConfig把init-param全部拿出来,就是那些在web.xml配置在Filter标签里面的初始化参数
for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
String name = (String) e.next();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
//构造dispatcher
return new Dispatcher(filterConfig.getServletContext(), params);
}
Dispatcher.init
//Load configurations, including both XML and zero-configuration strategies,
// and update optional settings, including whether to reload configurations and resource files.
public void init() {
if (configurationManager == null) {
configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
}
try {
init_FileManager();
init_DefaultProperties(); //加入默认配置供给器,解析org/apache/struts2/default.properties
init_TraditionalXmlConfigurations(); // [2] struts-default.xml
init_LegacyStrutsProperties(); // [3] struts-plugin.xml
init_CustomConfigurationProviders(); // [5] struts.properties
init_FilterInitParameters() ; // [6] default.properties
init_AliasStandardObjects() ; // [7] struts.custom.properties
// 预加载配置,构建容器
// 构造出容器,因此容器是拥有这些常量和配置里面指定的bean的实例化的能力。说到容器的实例化的能力,其实实例化的过程有一部分是依赖于容器的依赖注入功能的,容器实例化类的时候是通过构造函数注入(ConstructorInjector)方式,通过反射获取指定类的构造函数,如果有无参数的构造函数就直接使用无参数构造函数完成初始化,如果只有有参数的构造函数,就分析参数上面有没有注入的标志,如果有就通过容器的参数注入(ParameterInjector)方式注入依赖值给这个参数,然后通过这些有值的参数调用这个构造函数,达到实例化的目地,实例化之后还需要对其成员进行注入依赖,容器对成员注入是使用成员注入(MemberInjector,分别有MethodInjector和FieldInjector,针对方法还是属性判断使用哪一种方式)的方式,对静态成员还有静态注入(StaticInjector,其实质是通过MemberInjector实现的,只不过不需要穿入实例对象)方式;对于容器提供的依赖注入能力,其前提条件就是使用注入的类要配置在bean里面,也就是说类要交给容器管理,并且在使用注入依赖的属性、参数、方法要使用@Inject注解去指明,否则没有办法注入,或者注入了null,或者出现异常。
然后通过container.getInstance(ObjectFactory.class) , 创建出我们的ObjectFactory,使用默认工厂对象创建这个ObjectFactory的实例,根据之前的介绍,可以看到struts2的struts.objectFactory被注释了,并没有指定,意味着是使用struts作为name的ObjectFactory的bean,就是使用org.apache.struts2.impl.StrutsObjectFactory作为真正的ObjectFactory实现。
我们之后的Action、Result、Interceptor等等一系列与我们密切相关的对象都是通过ObjectFactory创建的,它可以说是struts的心脏。**ObjectFactory实例化对象的默认方式不是使用容器的,而是通过一般的Class.newIntance()方法**,而注入才是借助容器的inject(Container.inject)方法给实例化后的对象注入依赖值,对于这样的操作,一个解析就是避免影响核心类的性能,如果把所有的Action都交给容器来管理,会造成容器过于庞大,因为有时候Action可能会是成千上万的,这样会拖慢容器的执行;另外一个原因,因为Action、Result、Interceptor等都是需要多例的,而非单例,方便起见就直接用ObjectFactory,通过Class.newIntance()创建一个实例好了;估计还有一个方面想就是为了扩展,因为如果都交给容器,其他集成框架无法获取到struts2的容器的,无法操作他,所以为了扩展就使用了Class.newIntance()方法实例化。
**Container** container = init_PreloadConfiguration();
container.inject(this);
init_CheckWebLogicWorkaround(container);
//触发监听器初始化事件
if (!dispatcherListeners.isEmpty()) {
for (DispatcherListener l : dispatcherListeners) {
l.dispatcherInitialized(this);
}
}
errorHandler.init(servletContext);
} catch (Exception ex) {
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}
Dispatcher.init_CustomConfigurationProviders
//加入自定义的供给器
private void init_CustomConfigurationProviders() {
String configProvs = initParams.get("configProviders");
if (configProvs != null) {
String[] classes = configProvs.split("\\s*[,]\\s*");
for (String cname : classes) {
try {
Class cls = ClassLoaderUtil.loadClass(cname, this.getClass());
ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
if (prov instanceof ServletContextAwareConfigurationProvider) {
((ServletContextAwareConfigurationProvider)prov).initWithContext(servletContext);
}
configurationManager.addContainerProvider(prov);
} catch (InstantiationException e) {
throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Unable to access provider: "+cname, e);
} catch (ClassNotFoundException e) {
throw new ConfigurationException("Unable to locate provider class: "+cname, e);
}
}
}
}
从上面代码可以看出struts2对配置文件的加载顺序:
1、org/apache/struts2/default.properties
2、struts-default.xml
3、struts-plugin.xml(如果有)
4、struts.xml
5、struts.properties(如果有)
6、default.properties(如果有)
7、struts.custom.properties(如果有)
org/apache/struts2/default.properties是配置了struts2的常量的默认值,比如struts.i18n.encoding 、struts.action.extension、struts.i18n.reload等常量。
struts-default.xml里面配置了struts2的核心bean,以及用constant配置的常量,比如ObjectFactory、ActionProxyFactory、ActionMapper等bean。
struts-plugin.xml是struts2的插件的配置文件,比如struts2-spring-plugin。
struts.xml是使用struts2时候基本配置,比如action、result、interceptor、constant等。
struts.properties、default.properties、struts.custom.properties是原来旧版本的一些配置。
注意:后面加载的配置文件可以覆盖前面的配置文件的配置
从上面代码可以知道,init_XXXX的方法是为配置管理器收集配置供给器的信息,而最后调用init_PreloadConfiguration方法,构建出配置对象和容器,他们是struts2中非常重要的两个对象,配置对象里面包含了struts2的所有的配置信息,容器里面包含了struts2中所有的对象的工厂对象。以后使用到的Action、拦截器(Interceptor)、Result等对象都是通过他们两个协同创建出来的。
全部的配置都在这里解析完成,并能够组织起来,构成运行时的结构了
#第二部分:doFilter的工作过程
*StrutsPrepareAndExecuteFilter.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);
if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
chain.doFilter(request, response);
} else {
LOG.trace("Checking if {} is a static resource", uri);
*protected ExecuteOperations execute;*
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
LOG.trace("Assuming uri {} as a normal action", uri);
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
request = prepare.wrapRequest(request);
`**ActionMapping**` mapping = prepare.findActionMapping(request, response, true);
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);
execute.executeAction(request, response, mapping);
}
}
}
} finally {
prepare.cleanupRequest(request);
}
}
*ActionMapping*
public ActionMapping(String name, String namespace, String method, Map<String, Object> params) {
this.name = name;
this.namespace = namespace;
this.method = method;
this.params = params;
}
*// Tries to execute a request for a static resource*
public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
// there is no action in this request, should we look for a static resource?
String resourcePath = RequestUtils.getServletPath(request);
if ("".equals(resourcePath) && null != request.getPathInfo()) {
resourcePath = request.getPathInfo();
}
StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
if (staticResourceLoader.canHandle(resourcePath)) {
staticResourceLoader.findStaticResource(resourcePath, request, response);
// The framework did its job here
return true;
} else {
// this is a normal request, let it pass through
return false;
}
}
*ExecuteOperations类*
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
dispatcher.serviceAction(request, response, mapping);
}
*Dispatcher类*
//Dispatcher.serviceAction是我们所有action的统一处理入口,进入这个方法,就是开始了action的处理了。Dispatcher.serviceAction主要是完成以下几个工作,
1、创建Action对象,以代理对象的形式
2、执行mapping.execute或者开始调用拦截器(就是proxy.execute);如果执行了mapping.execute(执行Result返回客户端),就停止下面操作了
3、通过反射调用Action的指定方法,并返回
4、执行Result返回客户端,可以是页面(视图)跳转或者其他操作
这些都是重点啊!我们先总体看看Dispatcher.serviceAction方法
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
throws ServletException {
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,这里的ValueStack是我们后面的action用到的那个ValueStack*
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
boolean nullStack = stack == null;
if (nullStack) {
//用指定的ValueStack来创建一个新的ActionContext,并设置为本线程的ActionContext,
//注意这里已经不是之前的引导容器创建的ActionContext了,而是我们action使用的那个ActionContext,
//引导容器的ActionContext已经在DefaultConfiguration.reloadContainer方法退出时候被清空了,
//现在的ValueStack和ActionContext都是全新的。
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);
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
//创建ActionProxy动态代理
**ActionProxy** proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
// if the ActionMapping says to go straight to a result, do it!
**if (mapping.getResult() != null) {
//匹配到的action方法比如 login
如果是result.execute直接执行的情况,到这里doFilter对一个请求的处理过程就执行完了
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
//action中默认执行的方法
//proxy.execute,我们action执行的过程,当然在action真正执行之前,当然还有拦截器的执行,然后action执行完后返回一个resultCode,再根据resultCode找到对应的Result,执行页面(视图)跳转,返回客户端之前当然还会生成视图
proxy.execute();
}**
// If there was a previous value stack then set it back onto the request
if (!nullStack) {
//设置值栈
//再把修改过的ValueStack设置到request中,在Result之后返回视图时候可以用得着这些参数,
//因为Action执行方法时候可能会改变了ValueStack的里面的值
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch (ConfigurationException e) {
logConfigurationException(request, e);
sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
} catch (Exception e) {
e.printStackTrace();
if (handleException || devMode) {
sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
} else {
throw new ServletException(e);
}
} finally {
UtilTimerStack.pop(timerKey);
}
}
*DefaultActionInvocatio默认action代理类*
public String invoke() throws Exception {
String profileKey = "invoke: ";
try {
UtilTimerStack.push(profileKey);
//防止多次执行action,保证action只执行一次
if (executed) {
throw new IllegalStateException("Action has already executed");
}
//通过之前的拦截器映射迭代器迭代取出拦截器映射对象
if (interceptors.hasNext()) {
//获取拦截器映射对象
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
//通过映射对象获取拦截器,执行**Interceptor拦截器**,注意,intercept参数是当前的action调用对象,
//其实这里面也调用DefaultActionInvocation.invoke,他们这样是递归调用,通过这样的方法遍历完 interceptors
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
} finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
//当遍历完interceptors时候,执行正真的action操作,这里是一个重点
//返回的resultCode会用来匹配对应的Result
resultCode = invokeActionOnly();
}
//action是否已经被调用,用来判断是否第一次执行Result操作,这样写是因为action执行完后,
//拦截器会一个一个执行下面的操作,然后返回上一个调用的拦截器,就是按调用拦截器时候反过来的顺序执行,
//这是**递归的原理**,所以如果每个拦截器都执行Result那么程序就会出错,
//所以当action执行完Result之后,就通过这种方式不允许拦截器执行Result
if (!executed) { //第一次执行Result有效
//执行相关的监听器,Result执行前的监听器
if (preResultListeners != null) {
for (Object preResultListener : preResultListeners) {
PreResultListener listener = (PreResultListener) preResultListener;
String _profileKey = "preResultListener: ";
try {
UtilTimerStack.push(_profileKey);
listener.beforeResult(this, resultCode);
} finally {
UtilTimerStack.pop(_profileKey);
}
}
}
//如果支持执行Result,就执行,这个是构建action代理对象时候传入去的参数,是true
if (proxy.getExecuteResult()) {
//执行我们的Result,这里是另一个重点
executeResult();
}
//标识已经被执行过
executed = true;
}
//返回执行代码
return resultCode;
} finally {
UtilTimerStack.pop(profileKey);
}
}
当拦截器执行完时候,执行action的地方,在上面我们看到了 resultCode = invokeActionOnly()
*DefaultActionInvocation默认action代理类*
public String invokeActionOnly() throws Exception {
return invokeAction(getAction(), proxy.getConfig());
}
*DefaultActionInvocation默认action代理类*
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
String methodName = proxy.getMethod();
LOG.debug("Executing action method = {}", methodName);
String timerKey = "invokeAction: " + proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
Object methodResult;
try {
//获取action,然后根据methodName的名称,通过反射获取这个方法对象,这个是没有参数的方法
methodResult = ognlUtil.callMethod(methodName + "()", getStack().getContext(), action);
} catch (MethodFailedException e) {
// if reason is missing method, try checking UnknownHandlers
if (e.getReason() instanceof NoSuchMethodException) {
if (unknownHandlerManager.hasUnknownHandlers()) {
try {
//执行这个方法,设置action为执行实例,大多数情况都是执行这个方法,返回值大多数情况也是String类型
methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
} catch (NoSuchMethodException ignore) {
// throw the original one
throw e;
}
} else {
// throw the original one
throw e;
}
// throw the original exception as UnknownHandlers weren't able to handle invocation as well
if (methodResult == null) {
throw e;
}
} else {
// exception isn't related to missing action method, throw it
throw e;
}
}
return saveResult(actionConfig, methodResult);
} catch (NoSuchPropertyException e) {
throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
} catch (MethodFailedException e) {
// We try to return the source exception.
Throwable t = e.getCause();
if (actionEventListener != null) {
String result = actionEventListener.handleException(t, getStack());
if (result != null) {
return result;
}
}
if (t instanceof Exception) {
throw (Exception) t;
} else {
throw e;
}
} finally {
UtilTimerStack.pop(timerKey);
}
}
从上面可以看出,这个就是我们的Action的方法执行的地方,然后根据返回值,如果这个值是Result类型,就直接用这个Result来执行会面的操作;如果是String类型,就是resultCode,就用这个resultCode来获取Result,就是匹配result标签里面的name,这些配置早就储存在result配置对象了,然后用获取到的Result执行下面的操作,我们回到DefaultActionInvocation.invoke,看executeResult里面的处理过程
*DefaultActionInvocation默认action代理类*
public String invoke() throws Exception {
String profileKey = "invoke: ";
try {
UtilTimerStack.push(profileKey);
if (executed) {
throw new IllegalStateException("Action has already executed");
}
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
} finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
resultCode = **invokeActionOnly**();
}
// this is needed because the result will be executed, then control will return to the Interceptor, which will
// return above and flow through again
if (!executed) {
if (preResultListeners != null) {
LOG.trace("Executing PreResultListeners for result [{}]", result);
for (Object preResultListener : preResultListeners) {
PreResultListener listener = (PreResultListener) preResultListener;
String _profileKey = "preResultListener: ";
try {
UtilTimerStack.push(_profileKey);
listener.beforeResult(this, resultCode);
}
finally {
UtilTimerStack.pop(_profileKey);
}
}
}
// now execute the result, if we're supposed to
if (proxy.getExecuteResult()) {
**executeResult();**
}
executed = true;
}
return resultCode;
}
finally {
UtilTimerStack.pop(profileKey);
}
}
*DefaultActionInvocation默认action代理类*
private void executeResult() throws Exception {
//先创建一个Result
result = createResult();
String timerKey = "executeResult: " + getResultCode();
try {
UtilTimerStack.push(timerKey);
if (result != null) {
//如果创建成功
result.execute(this);
} else if (resultCode != null && !Action.NONE.equals(resultCode)) { //如果有返回值,并且Action不是没有视图的就抛出异常
throw new ConfigurationException("No result defined for action " + getAction().getClass().getName() + " and result " + getResultCode(), proxy.getConfig());
} else { //没有视图,就是通过response直接写回响应给客户端哪些情况,比如json
if (LOG.isDebugEnabled()) {
LOG.debug("No result returned for action {} at {}", getAction().getClass().getName(), proxy.getConfig().getLocation());
}
}
} finally {
UtilTimerStack.pop(timerKey);
}
}
“`
上面情况我们先看看createResult方法,我们假设返回的Result是一个org.apache.struts2.dispatcher.ServletDispatcherResult类型,就是默认类型,Action的默认页面(视图)跳转方式,需要返回值,有视图
当创建Result成功就可以执行了,前面说过假设是返回默认的Result ———— org.apache.struts2.dispatcher.ServletDispatcherResult,我们针对ServletDispatcherResult.execute来展开
result执行完了,跳转到指定的页面(视图),然后正真生成好页面,再返回给客户端
好累呀