Struts2整体工作流程
先通过Struts2官网一张图整体把握其工作流程:
在Struts2较新版本中,核心控制器为StrutsPrepareAndExecuteFilter,而不再是FilterDispatcher,但Struts2的基本架构不变。
初始化
Struts2下的web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!-- Struts2核心控制器配置,一个Filter -->
<filter>
<filter-name>struts2</filter-name>
<!-- 注意在2.1.3以上版本需使用此class -->
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Struts2核心配置文件struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!-- 定义常量key-value,struts.properties文件中定义的属性会覆盖constant -->
<constant name="struts.test" value="test1"/>
<!-- package包含多个Action,一个Action对应一个类的某个方法
package的名字必须是唯一的,package可以扩展
name:package名称
extends:继承的父package的配置
abstract:设置package的属性为抽象的,抽象的package不能定义action,值true:false
namespace:package命名空间,例如/test映射的url地址为http://localhost:8080/struts2/test/XX.action
-->
<package name="org_test_action" namespace="/test" extends="struts-default">
<!-- 定义拦截器、拦截器栈 -->
<interceptors>
<interceptor name="logInterceptor" class="org.test.action.LogInterceptor"/>
<interceptor name="timeInterceptor" class="org.test.action.TimeInterceptor" />
<interceptor-stack name="interStack">
<interceptor-ref name="logInterceptor" />
<interceptor-ref name="timeInterceptor"/>
</interceptor-stack>
</interceptors>
<!-- 定义org_test_action package下的默认拦截器,当action没有定义拦截器的时候会用到 -->
<default-interceptor-ref name="timeInterceptor"/>
<!-- 定义全局的exception映射 -->
<global-exception-mappings>
<exception-mapping result="error" exception="java.lang.Exception"></exception-mapping>
</global-exception-mappings>
<!-- 一个Action对应一个类的某个方法 -->
<action name="testaction" class="org.test.action.TestAction" method="execute">
<!-- testaction的异常映射,范围包括RuntimeException及其子类 -->
<exception-mapping result="error" exception="java.lang.RuntimeException"></exception-mapping>
<!-- 拦截器有重叠的情况下则合并 -->
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="interStack"/>
<result name="success">/WEB-INF/page/test.jsp</result>
<result name="error">/WEB-INF/page/error.jsp</result>
<!-- 设置TestAction的url属性 -->
<param name="url">http://www.sina.com</param>
</action>
</package>
</struts>
初始化调用栈
时序图
初始化整体流程:
init_PreloadConfiguration详细过程:
相关源码
/** StrutsPrepareAndExecuteFilter.java */
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
Dispatcher dispatcher = null;
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
// 初始化核心工作,解析相关配置文件,创建Container等
dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
prepare = new PrepareOperations(dispatcher);
execute = new ExecuteOperations(dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
// 清空ContainerHolder中缓存的Container
dispatcher.cleanUpAfterInit();
}
// ActionContext.setContext(null);
init.cleanup();
}
}
/** InitOperations.java */
private Dispatcher createDispatcher( HostConfig filterConfig ) {
// 获取web.xml中<filter>配置中的<init-param>键值对
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);
}
// new Dispatcher
return new Dispatcher(filterConfig.getServletContext(), params);
}
/** Dispatcher.java */
protected ConfigurationManager createConfigurationManager(String name) {
return new ConfigurationManager(name);
}
// configurationManager添加FileManagerProvider、FileManagerFactoryProvider
private void init_FileManager() throws ClassNotFoundException {
// configurationManager添加FileManagerProvider
if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER)) {
final String fileManagerClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER);
final Class<FileManager> fileManagerClass = (Class<FileManager>) Class.forName(fileManagerClassName);
if (LOG.isInfoEnabled()) {
LOG.info("Custom FileManager specified: #0", fileManagerClassName);
}
configurationManager.addContainerProvider(new FileManagerProvider(fileManagerClass, fileManagerClass.getSimpleName()));
} else {
// add any other Struts 2 provided implementations of FileManager
configurationManager.addContainerProvider(new FileManagerProvider(JBossFileManager.class, "jboss"));
}
// configurationManager添加FileManagerFactoryProvider
if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY)) {
final String fileManagerFactoryClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY);
final Class<FileManagerFactory> fileManagerFactoryClass = (Class<FileManagerFactory>) Class.forName(fileManagerFactoryClassName);
if (LOG.isInfoEnabled()) {
LOG.info("Custom FileManagerFactory specified: #0", fileManagerFactoryClassName);
}
configurationManager.addContainerProvider(new FileManagerFactoryProvider(fileManagerFactoryClass));
}
}
// configurationManager添加DefaultPropertiesProvider,用于加载org/apache/struts2/default.properties
private void init_DefaultProperties() {
configurationManager.addContainerProvider(new DefaultPropertiesProvider());
}
// configurationManager添加XmlConfigurationProvider,用于加载struts-default.xml,struts-plugin.xml,struts.xml
private void init_TraditionalXmlConfigurations() {
String configPaths = initParams.get("config");
if (configPaths == null) {
//configPaths = "struts-default.xml,struts-plugin.xml,struts.xml"
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
String[] files = configPaths.split("\\s*[,]\\s*");
for (String file : files) {
if (file.endsWith(".xml")) {
if ("xwork.xml".equals(file)) {
configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
} else {
configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException("Invalid configuration file name");
}
}
}
protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);
}
//configurationManager添加PropertiesConfigurationProvider,用于加载struts.properties
private void init_LegacyStrutsProperties() {
configurationManager.addContainerProvider(new PropertiesConfigurationProvider());
}
// 初始化<filter>配置中的<init-param> configProviders配置,添加到configurationManager,用于加载用户自定义的ConfigurationProvider
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();
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);
}
}
}
}
// configurationManager添加ConfigurationProvider,用于获取filter中initParams配置的所有键值对
private void init_FilterInitParameters() {
configurationManager.addContainerProvider(new ConfigurationProvider() {
public void destroy() {
}
public void init(Configuration configuration) throws ConfigurationException {
}
public void loadPackages() throws ConfigurationException {
}
public boolean needsReload() {
return false;
}
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
props.putAll(initParams);
}
});
}
// configurationManager添加DefaultBeanSelectionProvider,用于创建bean factory,以及关联别名
private void init_AliasStandardObjects() {
configurationManager.addContainerProvider(new DefaultBeanSelectionProvider());
}
// 创建container,解析struts.xml、struts.properties等配置文件
private Container init_PreloadConfiguration() {
Container container = getContainer();
boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
LocalizedTextUtil.setReloadBundles(reloadi18n);
boolean devMode = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_DEVMODE));
LocalizedTextUtil.setDevMode(devMode);
return container;
}
public Container getContainer() {
// 先查看ContainerHolder的ThreadLocal<Container> instance缓存
if (ContainerHolder.get() != null) {
return ContainerHolder.get();
}
ConfigurationManager mgr = getConfigurationManager();
if (mgr == null) {
throw new IllegalStateException("The configuration manager shouldn't be null");
} else {
// 获取struts.xml、struts.properties等配置文件解析后的Configuration配置
Configuration config = mgr.getConfiguration();
if (config == null) {
throw new IllegalStateException("Unable to load configuration");
} else {
Container container = config.getContainer();
// 缓存container到ContainerHolder
ContainerHolder.store(container);
return container;
}
}
}
public ConfigurationManager getConfigurationManager() {
return configurationManager;
}
/** ConfigurationManager.java */
public synchronized Configuration getConfiguration() {
if (configuration == null) {
// 创建DefaultConfiguration
setConfiguration(createConfiguration(defaultFrameworkBeanName));
try {
// 利用ConfigurationManager中的ContainerProviders加载Container
configuration.reloadContainer(getContainerProviders());
} catch (ConfigurationException e) {
setConfiguration(null);
throw new ConfigurationException("Unable to load configuration.", e);
}
} else {
conditionalReload();
}
return configuration;
}
protected Configuration createConfiguration(String beanName) {
return new DefaultConfiguration(beanName);
}
// 获取ConfigurationManager中的ContainerProviders
public List<ContainerProvider> getContainerProviders() {
providerLock.lock();
try {
if (containerProviders.size() == 0) {
containerProviders.add(new XWorkConfigurationProvider());
containerProviders.add(new XmlConfigurationProvider("xwork.xml", false));
}
return containerProviders;
} finally {
providerLock.unlock();
}
}
/** DefaultConfiguration.java */
public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
// 先清空配置缓存
packageContexts.clear();
loadedFileNames.clear();
List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();
ContainerProperties props = new ContainerProperties();
ContainerBuilder builder = new ContainerBuilder();
// 构建ContainerImpl,完成singletonFactories中单例的实例化
Container bootstrap = createBootstrapContainer(providers);
for (final ContainerProvider containerProvider : providers)
{
bootstrap.inject(containerProvider);
containerProvider.init(this);
// 初步解析struts.properties、struts.xml等配置文件
containerProvider.register(builder, props);
}
// 缓存Constants factories
props.setConstants(builder);
// builder缓存Configuration.class factory
builder.factory(Configuration.class, new Factory<Configuration>() {
public Configuration create(Context context) throws Exception {
return DefaultConfiguration.this;
}
});
ActionContext oldContext = ActionContext.getContext();
try {
// Set the bootstrap container for the purposes of factory creation
// 创建ActionContext
setContext(bootstrap);
// 用初步解析struts.properties、struts.xml等配置文件的builder创建container
container = builder.create(false);
setContext(container);
objectFactory = container.getInstance(ObjectFactory.class);
// Process the configuration providers first
for (final ContainerProvider containerProvider : providers)
{
if (containerProvider instanceof PackageProvider) {
// 依赖注入
container.inject(containerProvider);
// 具体解析<package>,解析到DefaultConfiguration的Map<String, PackageConfig> packageContexts中
((PackageProvider)containerProvider).loadPackages();
packageProviders.add((PackageProvider)containerProvider);
}
}
// Then process any package providers from the plugins
Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
for (String name : packageProviderNames) {
PackageProvider provider = container.getInstance(PackageProvider.class, name);
provider.init(this);
provider.loadPackages();
packageProviders.add(provider);
}
// 获取运行时的ActionConfig,其整合了packageConfig、actionConfig
rebuildRuntimeConfiguration();
} finally {
if (oldContext == null) {
ActionContext.setContext(null);
}
}
return packageProviders;
}
protected Container createBootstrapContainer(List<ContainerProvider> providers) {
// 临时ContainerBuilder
ContainerBuilder builder = new ContainerBuilder();
boolean fmFactoryRegistered = false;
// ContainerBuilder添加factories、singletonFactories缓存
for (ContainerProvider provider : providers) {
if (provider instanceof FileManagerProvider) {
provider.register(builder, null);
}
if (provider instanceof FileManagerFactoryProvider) {
provider.register(builder, null);
fmFactoryRegistered = true;
}
}
builder.factory(ObjectFactory.class, Scope.SINGLETON);
builder.factory(ActionFactory.class, DefaultActionFactory.class, Scope.SINGLETON);
builder.factory(ResultFactory.class, DefaultResultFactory.class, Scope.SINGLETON);
builder.factory(InterceptorFactory.class, DefaultInterceptorFactory.class, Scope.SINGLETON);
builder.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class, Scope.SINGLETON);
builder.factory(ConverterFactory.class, DefaultConverterFactory.class, Scope.SINGLETON);
builder.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class, Scope.SINGLETON);
builder.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON);
if (!fmFactoryRegistered) {
builder.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON);
}
builder.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON);
builder.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON);
builder.factory(XWorkConverter.class, Scope.SINGLETON);
builder.factory(ConversionPropertiesProcessor.class, DefaultConversionPropertiesProcessor.class, Scope.SINGLETON);
builder.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON);
builder.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON);
builder.factory(TypeConverterCreator.class, DefaultTypeConverterCreator.class, Scope.SINGLETON);
builder.factory(TypeConverterHolder.class, DefaultTypeConverterHolder.class, Scope.SINGLETON);
builder.factory(XWorkBasicConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, XWorkConstants.COLLECTION_CONVERTER, CollectionConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, XWorkConstants.ARRAY_CONVERTER, ArrayConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, XWorkConstants.DATE_CONVERTER, DateConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, XWorkConstants.NUMBER_CONVERTER, NumberConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, XWorkConstants.STRING_CONVERTER, StringConverter.class, Scope.SINGLETON);
builder.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON);
builder.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON);
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
builder.factory(OgnlUtil.class, Scope.SINGLETON);
builder.constant(XWorkConstants.DEV_MODE, "false");
builder.constant(XWorkConstants.LOG_MISSING_PROPERTIES, "false");
builder.constant(XWorkConstants.ENABLE_OGNL_EVAL_EXPRESSION, "false");
builder.constant(XWorkConstants.ENABLE_OGNL_EXPRESSION_CACHE, "true");
builder.constant(XWorkConstants.RELOAD_XML_CONFIGURATION, "false");
// 构建ContainerImpl
return builder.create(true);
}
/** ContainerBuilder.java */
public Container create(boolean loadSingletons) {
ensureNotCreated();
created = true;
// 带factories参数构建ContainerImpl
final ContainerImpl container = new ContainerImpl(
new HashMap<Key<?>, InternalFactory<?>>(factories));
// singletonFactorie单例实例化
if (loadSingletons) {
container.callInContext(new ContainerImpl.ContextualCallable<Void>() {
public Void call(InternalContext context) {
for (InternalFactory<?> factory : singletonFactories) {
factory.create(context);
}
return null;
}
});
}
container.injectStatics(staticInjections);
return container;
}
/** ContainerImpl.java */
<T> T callInContext( ContextualCallable<T> callable ) {
Object[] reference = localContext.get();
if (reference[0] == null) {
// new InternalContext(this),创建InternalContext环境
reference[0] = new InternalContext(this);
try {
// 调用callable
return callable.call((InternalContext) reference[0]);
} finally {
// Only remove the context if this call created it.
reference[0] = null;
// WW-3768: ThreadLocal was not removed
localContext.remove();
}
} else {
// Someone else will clean up this context.
return callable.call((InternalContext) reference[0]);
}
}
/** DefaultConfiguration.java */
protected ActionContext setContext(Container cont) {
ActionContext context = ActionContext.getContext();
if (context == null) {
// 创建ValueStack
ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
// 创建ActionContext
context = new ActionContext(vs.getContext());
ActionContext.setContext(context);
}
return context;
}
/** DefaultDispatcherErrorHandler.java */
public void init(ServletContext ctx) {
try {
freemarker.template.Configuration config = freemarkerManager.getConfiguration(ctx);
template = config.getTemplate("/org/apache/struts2/dispatcher/error.ftl");
} catch (IOException e) {
throw new StrutsException(e);
}
}
/** InitOperations.java */
public StaticContentLoader initStaticContentLoader( HostConfig filterConfig, Dispatcher dispatcher ) {
StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
loader.setHostConfig(filterConfig);
return loader;
}
一次http请求处理流程
调用栈
整体流程
一次http请求处理流程:
DefaultActionProxyFactory创建createActionProxy:
ActionInvocation类结构图:
相关源码
/** StrutsPrepareAndExecuteFilter.java */
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 {
// 设置request CharacterEncoding为UTF-8;设置response Locale(zh_CN)
prepare.setEncodingAndLocale(request, response);
// 创建ActionContext
prepare.createActionContext(request, response);
// 单例缓存dispatcher
prepare.assignDispatcherToThread();
// wrap request
request = prepare.wrapRequest(request);
// 根据请求url,创建ActionMapping
ActionMapping mapping = prepare.findActionMapping(request, response, true);
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
// 真正处理http请求
execute.executeAction(request, response, mapping);
}
}
} finally {
// ActionContext\Dispatcher\ContainerHolder单例缓存清空
prepare.cleanupRequest(request);
}
}
/** PrepareOperations.java */
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;
}
ActionContext oldContext = ActionContext.getContext();
// 创建ActionContext
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
ActionContext.setContext(ctx);
return ctx;
}
public void assignDispatcherToThread() {
Dispatcher.setInstance(dispatcher);
}
public HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException {
HttpServletRequest request = oldRequest;
try {
// Wrap request first, just in case it is multipart/form-data
// parameters might not be accessible through before encoding (ww-1278)
request = dispatcher.wrapRequest(request);
} catch (IOException e) {
throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e);
}
return request;
}
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
if (mapping == null || forceLookup) {
try {
// 根据请求url,创建ActionMapping
mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
if (mapping != null) {
// 设置request属性struts.actionMapping为mapping
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
}
} catch (Exception ex) {
dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
}
}
return mapping;
}
/** Dispatcher.java */
// wrap Request,主要分清文件请求还是一般请求
public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException {
// don't wrap more than once
if (request instanceof StrutsRequestWrapper) {
return request;
}
String content_type = request.getContentType();
if (content_type != null && content_type.contains("multipart/form-data")) {
// 文件上传
MultiPartRequest mpr = getMultiPartRequest();
LocaleProvider provider = getContainer().getInstance(LocaleProvider.class);
request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup);
} else {
// 一般请求
request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
}
return request;
}
/** DefaultActionMapper.java */
public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
ActionMapping mapping = new ActionMapping();
// 获取请求url
String uri = RequestUtils.getUri(request);
int indexOfSemicolon = uri.indexOf(";");
uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;
// 去掉uri中的.action后缀
uri = dropExtension(uri, mapping);
if (uri == null) {
return null;
}
// 解析请求url中的Namespace和action name
parseNameAndNamespace(uri, mapping, configManager);
handleSpecialParameters(request, mapping);
return parseActionName(mapping);
}
/** ExecuteOperations.java */
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
// 处理请求工作委托给dispatcher
dispatcher.serviceAction(request, response, mapping);
}
/** Dispatcher.java */
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
throws ServletException {
// 创建属性Map,包括request、session、application等范围的属性及其他属性
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添加ValueStack属性
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设置struts.valueStack属性
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 {
// 处理http请求
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);
}
}
/** DefaultActionProxy.java */
protected void prepare() {
String profileKey = "create DefaultActionProxy: ";
try {
UtilTimerStack.push(profileKey);
// 根据namespace、actionName获取Runtime ActionConfig
config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);
if (config == null && unknownHandlerManager.hasUnknownHandlers()) {
config = unknownHandlerManager.handleUnknownAction(namespace, actionName);
}
if (config == null) {
throw new ConfigurationException(getErrorMessage());
}
// 确定执行action的哪个method
resolveMethod();
if (!config.isAllowedMethod(method)) {
throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);
}
// DefaultActionInvocation init
invocation.init(this);
} finally {
UtilTimerStack.pop(profileKey);
}
}
/** DefaultActionInvocation.java */
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();
// 将DefaultActionInvocation actionInvocation put到actionContext
if (actionContext != null) {
actionContext.setActionInvocation(this);
}
// 在DefaultActionInvocation中,实例化action对象,且完成依赖注入
createAction(contextMap);
if (pushAction) {
// 将action对象push到ValueStack
stack.push(action);
// OgnlContext put action对象
contextMap.put("action", action);
}
// 用OgnlContext创建ActionContext
invocationContext = new ActionContext(contextMap);
// 用actionName设置invocationContext名称
invocationContext.setName(proxy.getActionName());
// get a new List so we don't get problems with the iterator if someone changes the list
// new interceptorList
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
}
protected Map<String, Object> createContextMap() {
Map<String, Object> contextMap;
if ((extraContext != null) && (extraContext.containsKey(ActionContext.VALUE_STACK))) {
// 从extraContext中获取ValueStack
stack = (ValueStack) extraContext.get(ActionContext.VALUE_STACK);
if (stack == null) {
throw new IllegalStateException("There was a null Stack set into the extra params.");
}
// 获取ValueStack的OgnlContext
contextMap = stack.getContext();
} else {
// create the value stack
// this also adds the ValueStack to its context
stack = valueStackFactory.createValueStack();
// create the action context
contextMap = stack.getContext();
}
// 将extraContext中的key-value对put到ValueStack的OgnlContext
if (extraContext != null) {
contextMap.putAll(extraContext);
}
// contextMap put DefaultActionInvocation actionInvocation属性
contextMap.put(ActionContext.ACTION_INVOCATION, this);
// contextMap put container属性
contextMap.put(ActionContext.CONTAINER, container);
return contextMap;
}
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);
}
}
/** ObjectFactory.java */
public Object buildAction(String actionName, String namespace, ActionConfig config, Map<String, Object> extraContext) throws Exception {
return actionFactory.buildAction(actionName, namespace, config, extraContext);
}
/** DefaultActionFactory.java */
// 实例化action对象的工作委托给objectFactory
public Object buildAction(String actionName, String namespace, ActionConfig config, Map<String, Object> extraContext) throws Exception {
return objectFactory.buildBean(config.getClassName(), extraContext);
}
/** ObjectFactory.java */
public Object buildBean(String className, Map<String, Object> extraContext) throws Exception {
return buildBean(className, extraContext, true);
}
// 实例化action对象,且完成依赖注入
public Object buildBean(String className, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
// 加载Class
Class clazz = getClassInstance(className);
// 用反射实例化action对象
Object obj = buildBean(clazz, extraContext);
if (injectInternal) {
// 对action对象进行依赖注入
injectInternalBeans(obj);
}
return obj;
}
public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
return clazz.newInstance();
}
protected Object injectInternalBeans(Object obj) {
if (obj != null && container != null) {
container.inject(obj);
}
return obj;
}
/** StrutsActionProxy.java */
public String execute() throws Exception {
ActionContext previous = ActionContext.getContext();
// 将DefaultActionInvocation的ActionContext设置为当前线程的ActionContext
ActionContext.setContext(invocation.getInvocationContext());
try {
// DefaultActionInvocation调用
return invocation.invoke();
} finally {
if (cleanupContext)
ActionContext.setContext(previous);
}
}
/** DefaultActionInvocation.java */
public String invoke() throws Exception {
String profileKey = "invoke: ";
try {
UtilTimerStack.push(profileKey);
if (executed) {
throw new IllegalStateException("Action has already executed");
}
// 执行interceptor链
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 {
// 执行http请求对应的action
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) {
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()) {
// 在ServletDispatcherResult中,根据result finalLocation获取RequestDispatcher,进行页面跳转
// 本质上是设置HttpServletResponse返回的内容,后续interceptor链还可以修改HttpServletResponse的内容
executeResult();
}
executed = true;
}
return resultCode;
}
finally {
UtilTimerStack.pop(profileKey);
}
}
public String invokeActionOnly() throws Exception {
return invokeAction(getAction(), proxy.getConfig());
}
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
String methodName = proxy.getMethod();
if (LOG.isDebugEnabled()) {
LOG.debug("Executing action method = #0", methodName);
}
String timerKey = "invokeAction: " + proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
Object methodResult;
try {
// ognl利用反射执行method,获取methodResult
methodResult = ognlUtil.getValue(methodName + "()", getStack().getContext(), action);
} catch (MethodFailedException e) {
// if reason is missing method, try find version with "do" prefix
if (e.getReason() instanceof NoSuchMethodException) {
try {
String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1) + "()";
methodResult = ognlUtil.getValue(altMethodName, getStack().getContext(), action);
} catch (MethodFailedException e1) {
// if still method doesn't exist, try checking UnknownHandlers
if (e1.getReason() instanceof NoSuchMethodException) {
if (unknownHandlerManager.hasUnknownHandlers()) {
try {
methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
} catch (NoSuchMethodException e2) {
// 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 e1;
}
}
} 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);
}
}
/** OgnlUtil.java */
public Object getValue(final String name, final Map<String, Object> context, final Object root) throws OgnlException {
return compileAndExecute(name, context, new OgnlTask<Object>() {
public Object execute(Object tree) throws OgnlException {
return Ognl.getValue(tree, context, root);
}
});
}
private <T> Object compileAndExecute(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
Object tree;
if (enableExpressionCache) {
tree = expressions.get(expression);
if (tree == null) {
tree = Ognl.parseExpression(expression);
checkEnableEvalExpression(tree, context);
}
} else {
tree = Ognl.parseExpression(expression);
checkEnableEvalExpression(tree, context);
}
// ognl利用反射执行method
final T exec = task.execute(tree);
// if cache is enabled and it's a valid expression, puts it in
if(enableExpressionCache) {
expressions.putIfAbsent(expression, tree);
}
return exec;
}
/** DefaultActionInvocation.java */
private void executeResult() throws Exception {
// 获取action执行的ServletDispatcherResult
result = createResult();
String timerKey = "executeResult: " + getResultCode();
try {
UtilTimerStack.push(timerKey);
if (result != null) {
result.execute(this);
} else if (resultCode != null && !Action.NONE.equals(resultCode)) {
throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
+ " and result " + getResultCode(), proxy.getConfig());
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());
}
}
} finally {
UtilTimerStack.pop(timerKey);
}
}
public Result createResult() throws Exception {
if (explicitResult != null) {
Result ret = explicitResult;
explicitResult = null;
return ret;
}
ActionConfig config = proxy.getConfig();
Map<String, ResultConfig> results = config.getResults();
ResultConfig resultConfig = null;
try {
resultConfig = results.get(resultCode);
} catch (NullPointerException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Got NPE trying to read result configuration for resultCode [#0]", resultCode);
}
}
if (resultConfig == null) {
// If no result is found for the given resultCode, try to get a wildcard '*' match.
resultConfig = results.get("*");
}
if (resultConfig != null) {
try {
// new ServletDispatcherResult对象
return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("There was an exception while instantiating the result of type #0", e, resultConfig.getClassName());
}
throw new XWorkException(e, resultConfig);
}
} else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
}
return null;
}
/** ObjectFactory.java */
public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
return resultFactory.buildResult(resultConfig, extraContext);
}
/** StrutsResultFactory.java */
public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
String resultClassName = resultConfig.getClassName();
Result result = null;
if (resultClassName != null) {
// new ServletDispatcherResult对象
result = (Result) objectFactory.buildBean(resultClassName, extraContext);
Map<String, String> params = resultConfig.getParams();
if (params != null) {
setParameters(extraContext, result, params);
}
}
return result;
}
protected void setParameters(Map<String, Object> extraContext, Result result, Map<String, String> params) {
for (Map.Entry<String, String> paramEntry : params.entrySet()) {
try {
String name = paramEntry.getKey();
String value = paramEntry.getValue();
// ognl设置key-value
setParameter(result, name, value, extraContext);
} catch (ReflectionException ex) {
if (result instanceof ReflectionExceptionHandler) {
((ReflectionExceptionHandler) result).handle(ex);
}
}
}
}
protected void setParameter(Result result, String name, String value, Map<String, Object> extraContext) {
if (result instanceof ParamNameAwareResult) {
if (((ParamNameAwareResult) result).acceptableParameterName(name, value)) {
reflectionProvider.setProperty(name, value, result, extraContext, true);
}
} else {
reflectionProvider.setProperty(name, value, result, extraContext, true);
}
}
/** StrutsResultSupport.java */
public void execute(ActionInvocation invocation) throws Exception {
lastFinalLocation = conditionalParse(location, invocation);
doExecute(lastFinalLocation, invocation);
}
/** ServletDispatcherResult.java */
public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
if (LOG.isDebugEnabled()) {
LOG.debug("Forwarding to location " + finalLocation);
}
PageContext pageContext = ServletActionContext.getPageContext();
if (pageContext != null) {
pageContext.include(finalLocation);
} else {
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
// 根据result finalLocation获取RequestDispatcher,用于页面跳转
RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);
//add parameters passed on the location to #parameters
// see WW-2120
if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf("?") > 0) {
String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1);
Map<String, Object> parameters = getParameters(invocation);
Map<String, Object> queryParams = urlHelper.parseQueryString(queryString, true);
if (queryParams != null && !queryParams.isEmpty())
parameters.putAll(queryParams);
}
// if the view doesn't exist, let's do a 404
if (dispatcher == null) {
response.sendError(404, "result '" + finalLocation + "' not found");
return;
}
//if we are inside an action tag, we always need to do an include
Boolean insideActionTag = (Boolean) ObjectUtils.defaultIfNull(request.getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION), Boolean.FALSE);
// If we're included, then include the view
// Otherwise do forward
// This allow the page to, for example, set content type
if (!insideActionTag && !response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {
// 设置request struts.view_uri、struts.request_uri属性
request.setAttribute("struts.view_uri", finalLocation);
request.setAttribute("struts.request_uri", request.getRequestURI());
dispatcher.forward(request, response);
} else {
dispatcher.include(request, response);
}
}
}
通过源码分析,进一步了解Struts2的基本架构。