Struts2运行过程
1.Struts2启动过程
在Tomcat启动的时候对Struts2 访问的过程:
1.先访问StrutsPrepareAndExcuteFilter类中的init()方法
2.在init方法中调用InitOperations的initDispatcher()方法
3.调用Dispatcher中的init()方法
最终在init()方法中加载
1.default.properties 默认的常量配置文件
2.struts-default.xml 内部的xml
3.struts-plugin.xml 插件机制的xml文件
4.struts.xml 自己写的xml文件
注:后面三个xml文件的did约束是一样的,如果出现相同的项目后者覆盖前者
2.action请求时的过程
当我们在浏览器输入一个url进行action的访问时,tomcat会访问web.xml配置文件,又因为我们在web.xml这样定义拦截器:
<filter>
<filter-name>struts2</filter-name>
<filterclass>org.apache.struts2.dispatcher.ng.
filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
所以Struts2将进行StrutsPrepareAndExecuteFilter类的初始化
public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter
可以看到StrutsPrepareAndExecuteFilter实现了StrutsStatics 以及 Filter接口
下面看看StrutsStatics以及Filter接口声明了什么
public interface StrutsStatics {
/**
* Constant for the HTTP request object.
*/
public static final String HTTP_REQUEST = "com.opensymphony.xwork2.dispatcher.HttpServletRequest";
/**
* Constant for the HTTP response object.
*/
public static final String HTTP_RESPONSE = "com.opensymphony.xwork2.dispatcher.HttpServletResponse";
/**
* Constant for an HTTP {@link javax.servlet.RequestDispatcher request dispatcher}.
*/
public static final String SERVLET_DISPATCHER = "com.opensymphony.xwork2.dispatcher.ServletDispatcher";
/**
* Constant for the {@link javax.servlet.ServletContext servlet context} object.
*/
public static final String SERVLET_CONTEXT = "com.opensymphony.xwork2.dispatcher.ServletContext";
/**
* Constant for the JSP {@link javax.servlet.jsp.PageContext page context}.
*/
public static final String PAGE_CONTEXT = "com.opensymphony.xwork2.dispatcher.PageContext";
/** Constant for the PortletContext object */
public static final String STRUTS_PORTLET_CONTEXT = "struts.portlet.context";
/**
* Set as an attribute in the request to let other parts of the framework know that the invocation is happening inside an
* action tag
*/
public static final String STRUTS_ACTION_TAG_INVOCATION= "struts.actiontag.invocation";
}
从源码中我们可以看出在StrutsStatics 定义了一堆常量
从JavaDoc中我们可以了解到
/**
* Constants used by Struts. The constants can be used to get or set objects
* out of the action context or other collections.
*
* <p/>
*
* Example:
* <ul><code>ActionContext.getContext().put(HTTP_REQUEST, request);</code></ul>
* <p/>
* or
* <p/>
* <ul><code>
* ActionContext context = ActionContext.getContext();<br>
* HttpServletRequest request = (HttpServletRequest)context.get(HTTP_REQUEST);</code></ul>
*/
这些常量是用来对对象进行静态注入。
在StrutsStatics 中我们看出是一个常量接口,那么在Filter接口中又有什么方法呢?
public interface Filter {
//初始化
public void init(FilterConfig filterConfig) throws ServletException;
//执行过滤
public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;
//销毁
public void destroy();
}
通过Filter接口我们可以了解到filter的生命周期init-doFilter-destroy
下面来分析StrutsPrepareAndExecuteFilter类中的init方法
1.init()
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
Dispatcher dispatcher = null;
try {
//封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中
FilterHostConfig config = new FilterHostConfig(filterConfig);
// 初始化struts内部日志
init.initLogging(config);
//创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源
dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
//初始化类属性:prepare 、execute
prepare = new PrepareOperations(dispatcher);
execute = new ExecuteOperations(dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//回调空的postInit方法
postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
下面着重研究dispatcher = init.initDispatcher(config);
public Dispatcher initDispatcher( HostConfig filterConfig ) {
Dispatcher dispatcher = createDispatcher(filterConfig);
dispatcher.init();
return dispatcher;
}
private Dispatcher createDispatcher( HostConfig filterConfig ) {
Map<String, String> params = new HashMap<String, String>();
for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
String name = (String) e.next();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
return new Dispatcher(filterConfig.getServletContext(), params);
}
在创建dispatcher的时候会调用了一个HostCofig,那么HostCofig是干嘛的呢
public interface HostConfig {
/**
* @param key The parameter key
* @return The parameter value
*/
String getInitParameter(String key);
/**
* @return A list of parameter names
*/
Iterator<String> getInitParameterNames();
/**
* @return The servlet context
*/
ServletContext getServletContext();
}
三个方法分别是:
1.通过键获得值
2.获取属性名称的迭代器
3.获得servlet上下文
通过快捷键crlt+t来查看实现HostConfig接口的类
/**
* Host configuration that wraps FilterConfig
* FilterConfig的包装类
*/
public class FilterHostConfig implements HostConfig {
private FilterConfig config;
//构造方法
public FilterHostConfig(FilterConfig config) {
this.config = config;
}
//根据init-param配置的param-name获取param-value的值
public String getInitParameter(String key) {
return config.getInitParameter(key);
}
//返回初始化参数名的List的迭代器
public Iterator<String> getInitParameterNames() {
return MakeIterator.convert(config.getInitParameterNames());
}
//返回ServletContext
public ServletContext getServletContext() {
return config.getServletContext();
}
}
/**
* Host configuration that just holds a ServletContext
* 只存在ServletContext的配置类
*/
public class ListenerHostConfig implements HostConfig {
private ServletContext servletContext;
//构造函数
public ListenerHostConfig(ServletContext servletContext) {
this.servletContext = servletContext;
}
public String getInitParameter(String key) {
return null;
}
public Iterator<String> getInitParameterNames() {
return Collections.<String>emptyList().iterator();
}
//返回ServletContext
public ServletContext getServletContext() {
return servletContext;
}
}
/**
* Host configuration that wraps a ServletConfig
* ServletConfig包装类
*/
public class ServletHostConfig implements HostConfig {
private ServletConfig config;
//构造函数
public ServletHostConfig(ServletConfig config) {
this.config = config;
}
//根据init-param配置的param-name获取param-value的值
public String getInitParameter(String key) {
return config.getInitParameter(key);
}
//返回初始化参数名的List的迭代器
public Iterator<String> getInitParameterNames() {
return MakeIterator.convert(config.getInitParameterNames());
}
//返回ServletContext
public ServletContext getServletContext() {
return config.getServletContext();
}
}
在getInitParameterNames对配置文件中的属性名进行封装,并包装成迭代器进行返回
以上三个方法都是对配置文件进行解析,从而进行创建dispatch那么createDispatcher(filterConfig)是做了什么
private Dispatcher createDispatcher( HostConfig filterConfig ) {
Map<String, String> params = new HashMap<String, String>();
//根据迭代器中的值来对属性进行赋值封装成为一个Map,然后根据servlet上下文和参数Map构造Dispatcher
for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
String name = (String) e.next();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
return new Dispatcher(filterConfig.getServletContext(), params);
}
创建完了Dispatcher接着就要进行 dispatcher.init();初始化方法
而 Dispatcher初始化的过程,加载struts2的相关配置文件,将按照顺序逐一加载:default.properties,struts-default.xml,struts-plugin.xml,struts.xml,……
/**
* Load configurations, including both XML and zero-configuration strategies,
* and update optional settings, including whether to reload configurations and resource files.
* 加载配置文件包括如下...
*/
public void init() {
//如果configurationManager 不存在则创建configurationManager
if (configurationManager == null) {
configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
}
try {
//初始化文件管理器,用于加载配置文件
init_FileManager();
//加载org/apache/struts2/default.properties
init_DefaultProperties(); // [1]
init_TraditionalXmlConfigurations(); // [2]
init_LegacyStrutsProperties(); // [3]
//用户自己实现的ConfigurationProviders类
init_CustomConfigurationProviders(); // [5]
//Filter的初始化参数
init_FilterInitParameters() ; // [6]
//别名的类的加载
init_AliasStandardObjects() ; // [7]
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) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}
private void init_DefaultProperties() {
configurationManager.addContainerProvider(new DefaultPropertiesProvider());
}
addContainerProvider调用xwork包中的方法
public void addContainerProvider(ContainerProvider provider) {
if (!containerProviders.contains(provider)) {
containerProviders.add(provider);
providersChanged = true;
}
}
想providers容器添加一个provider那么DefaultPropertiesProvider中又有什么呢
/**
* Loads the default properties, separate from the usual struts.properties loading
*/
public class DefaultPropertiesProvider extends PropertiesConfigurationProvider {
public void destroy() {
}
public void init(Configuration configuration) throws ConfigurationException {
}
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
try {
PropertiesSettings defaultSettings = new PropertiesSettings("org/apache/struts2/default");
loadSettings(props, defaultSettings);
} catch (Exception e) {
throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e);
}
}
通过javaDoc和方法体可以知道DefaultPropertiesProvider 是对各种配置文件进行加载
init主要的方法就是这些,具体就不详解了。
2.doFilter()
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//父类向子类转:强转为http请求、响应
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
//如果prepare检测出request的uri与excludedPatterns中的pattern匹配则转发到链的下一个filter
chain.doFilter(request, response);
} else {
//设置编码格式:utf-8,值来自于org.apach.struts 包中的默认default.properties
prepare.setEncodingAndLocale(request, response);
//创建Action上下文 重点
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
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);
}
}
其中createActionContext是重点内容我们观察下其中的方法:
/**
* Creates the action context and initializes the thread local
* 创建action上下文并且初始化thread local
*/
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
ActionContext ctx;
Integer counter = 1;
Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
if (oldCounter != null) {
counter = oldCounter + 1;
}
//注意此处是从ThreadLocal中获取此ActionContext变量
ActionContext oldContext = ActionContext.getContext();
if (oldContext != null) {
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
} else {
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
ActionContext.setContext(ctx);
return ctx;
}
其中 dispatcher.createContextMap():
public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
ActionMapping mapping) {
// request map wrapping the http request objects
Map requestMap = new RequestMap(request);
// parameters map wrapping the http parameters. ActionMapping parameters are now handled and applied separately
Map params = new HashMap(request.getParameterMap());
// session map wrapping the http session
Map session = new SessionMap(request);
// application map wrapping the ServletContext
Map application = new ApplicationMap(servletContext);
Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response);
if (mapping != null) {
extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
}
return extraContext;
}
可以看出:
通过struts2的容器获取到valueStack对象:OnglValueStack
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
1、把request,session,application等封装成一些map,再把这些map放入到大map中
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
2.把大map的引用指向了ActionContext中的Map content;
ctx = new ActionContext(stack.getContext());
把ActionContext放入到了当前线程中ActionContext.setContext(ctx);
说明:因为把ActionContext放入到了当前线程中,所以valueStack也在线程中,这样数据就可以保证安全了并且在一个线程范围内可以共享数据
接下来的进行的方法request = prepare.wrapRequest(request);
对request进行了包装:返回的对象是:
1. StrutsRequestWrapper
2. MultPartRequestWrapper 文件上传 继承了 StrutsRequestWrapper
接下来进行的就是
ActionMapping mapping = prepare.findActionMapping(request, response, true);
那么ActionMapping是干什么的?
public class ActionMapping {
private String name;
private String namespace;
private String method;
private String extension;
private Map<String, Object> params;
private Result result;
发没发现跟sturts2.xml格式很想
<package name="users" namespace="/users" extends="default">
<action name="*_*" class="action.{1}Action" method="{2}">
<result name="login_success">/users/Users_login_success.jsp</result>
<result name="login_false">/users/Users_login.jsp</result>
<result name="logout_success">/users/Users_login.jsp</result>
<result name="input">/users/Users_login.jsp</result>
</action>
可见ActionMapping就是对struts2的配置文件的包装
同时也就可以明白findActionMapping就是对struts2.xml的解析
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
if (mapping == null || forceLookup) {
try {
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;
}
接下来就是进行execute.executeAction(request, response, mapping);
/**
* Executes an action
* @throws ServletException
*/
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
dispatcher.serviceAction(request, response, mapping);
}
从javadoc看出是执行一个action,继续看dispatcher.serviceAction(request, response, mapping);是怎么执行的
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
//根据request获取值栈
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中
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 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) {
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 proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);中
其中ActionProxyFactory.class是在struts-default.xml声明的
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="prefix" class="org.apache.struts2.impl.StrutsActionProxyFactory"/>
同时点开createActionProxy的实现方法
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
ActionInvocation inv = createActionInvocation(extraContext, true);
container.inject(inv);
return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
}
继续点到createActionProxy
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);
proxy.prepare();
return proxy;
}
}
继续 proxy.prepare();
protected void prepare() {
super.prepare();
}
继续
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());
}
resolveMethod();
if (!config.isAllowedMethod(method)) {
throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);
}
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());
createInterceptors(proxy);
}
最终目标
protected void createAction(Map<String, Object> contextMap) {
// load action
String timerKey = "actionCreate: " + proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
//创建action
action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
} catch (InstantiationException e) {
throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());
} catch (IllegalAccessException e) {
throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());
} catch (Exception e) {
String gripe;
if (proxy == null) {
gripe = "Whoa! No ActionProxy instance found in current ActionInvocation. This is bad ... very bad";
} else if (proxy.getConfig() == null) {
gripe = "Sheesh. Where'd that ActionProxy get to? I can't find it in the current ActionInvocation!?";
} else if (proxy.getConfig().getClassName() == null) {
gripe = "No Action defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
} else {
gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ", defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
}
gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
throw new XWorkException(gripe, e, proxy.getConfig());
} finally {
UtilTimerStack.pop(timerKey);
}
if (actionEventListener != null) {
action = actionEventListener.prepare(action, stack);
}
}
DefaultActionInvocation中的init方法
createAction(contextMap); 创建action
objectFactory.buildAction
stack.push(action); 把action放入到栈顶
contextMap.put(“action”, action);
把action放入到map中
List interceptorList = new ArrayList(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
获取这次请求所有的拦截器,并且返回拦截器的迭代器的形式
最后通过proxy.execute();
调用到
DefaultActionInvocation中的invoke方法
1、按照顺序的方式执行所有的拦截器
2、执行action中的方法
3、执行结果集
4、按照倒叙的方式执行拦截器