首先来看一下web.xml中的struts2的过滤器
<filter>
<filter-name>struts2</filter-name>
<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>
查看StrutsPrepareAndExecuteFilter这个过滤器,首先我们知道当tomcat服务器启动时,将会加载web.xml文件,加载到过滤器时就会执行过滤器的init方法,那么我们一起来看一下StrutsPrepareAndExecuteFilter的init()方法。
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
Dispatcher dispatcher = null;
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
这时我们看到 dispatcher = init.initDispatcher(config);创建了一个dispatcher对象,我们来查看一下initDispatcher();
public Dispatcher initDispatcher( HostConfig filterConfig ) {
Dispatcher dispatcher = createDispatcher(filterConfig);
dispatcher.init();
return dispatcher;
}
这里使用创建了一个dispatcher对象,查看一下createDispatcher方法。
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);
}
这里是将过滤器里配置的init-param参数传递给了Dispatcher对象的initParams。我们继续来查看initDispatcher方法中的dispatcher的init()方法。
public void init() {
if (configurationManager == null) {
configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
try {
init_FileManager();
init_DefaultProperties(); // [1]
init_TraditionalXmlConfigurations(); // [2]
init_LegacyStrutsProperties(); // [3]
init_CustomConfigurationProviders(); // [5]
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);
}
}
} catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}
查看一下创建ConfigurationManager的方法createConfigurationManager()
protected ConfigurationManager createConfigurationManager(String name) {
return new ConfigurationManager(name);
}
查看一下这个方法init_DefaultProperties()方法
private void init_DefaultProperties() {
configurationManager.addContainerProvider(new DefaultPropertiesProvider());
}
进入DefaultPropertiesProvider类中。
public class DefaultPropertiesProvider extends LegacyPropertiesConfigurationProvider {
public void destroy() {
}
public void init(Configuration configuration) throws ConfigurationException {
}
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
Settings defaultSettings = null;
try {
defaultSettings = new PropertiesSettings("org/apache/struts2/default");
} catch (Exception e) {
throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e);
}
loadSettings(props, defaultSettings);
}
}
这里我们看到这里有一个register方法中,这个方法中加载了struts2的default.properties文件。当然咯,这个方法调用的时机,待我慢慢道来,这时configurationManager对象的containerProviders加入了一个DefaultPropertiesProvider对象。接着我们来看dispatcher.init()方法中的init_TraditionalXmlConfigurations()这个方法。
private void init_TraditionalXmlConfigurations() {
String configPaths = initParams.get("config");
if (configPaths == null) {
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");
}
}
}
这里我们发现DEFAULT_CONFIGURATION_PATHS这个常量,而这个常量的值struts-default.xml,struts-plugin.xml,struts.xml,当然咯,这个是默认值,我们可以通过配置struts2过滤器的init-param来修改,只要设置param的名字config,在这里就可以取到,从而可以加载我们其他的配置文件,当然喽,这里我们推荐不要用init-param,如果有需求,可以这么做,在这里会通过这几个配置文件创建XmlConfigurationProvider或者StrutsXmlConfigurationProvider。当配置文件中有一个xwork.xml时,将会创建XmlConfigurationProvider,如果是其他的则是StrutsXmlConfigurationProvider。此时,我们先来看一下createXmlConfigurationProvider(file, false)方法和createStrutsXmlConfigurationProvider(file, false, servletContext)
protected XmlConfigurationProvider createXmlConfigurationProvider(String filename, boolean errorIfMissing) {
return new XmlConfigurationProvider(filename, errorIfMissing);
}
protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);
}
这里都是直接new出来的,那么我们来看一下这两个类的结构,发现,StrutsXmlConfigurationProvider继承XmlConfigurationProvider类,看代码,由于代码过多,就只贴出init和register方法和构造函数。
public class XmlConfigurationProvider implements ConfigurationProvider {
private List<Document> documents;
public XmlConfigurationProvider(String filename, boolean errorIfMissing) {
this.configFileName = filename;
this.errorIfMissing = errorIfMissing;
Map<String, String> mappings = new HashMap<String, String>();
mappings.put("-//Apache Struts//XWork 2.3//EN", "xwork-2.3.dtd");
mappings.put("-//Apache Struts//XWork 2.1.3//EN", "xwork-2.1.3.dtd");
mappings.put("-//Apache Struts//XWork 2.1//EN", "xwork-2.1.dtd");
mappings.put("-//Apache Struts//XWork 2.0//EN", "xwork-2.0.dtd");
mappings.put("-//Apache Struts//XWork 1.1.1//EN", "xwork-1.1.1.dtd");
mappings.put("-//Apache Struts//XWork 1.1//EN", "xwork-1.1.dtd");
mappings.put("-//Apache Struts//XWork 1.0//EN", "xwork-1.0.dtd");
setDtdMappings(mappings);
}
public void init(Configuration configuration) {
this.configuration = configuration;
this.includedFileNames = configuration.getLoadedFileNames();
loadDocuments(configFileName);
}
public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
Map<String, Node> loadedBeans = new HashMap<String, Node>();
for (Document doc : documents) {
Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for (int i = 0; i < childSize; i++) {
Node childNode = children.item(i);
if (childNode instanceof Element) {
Element child = (Element) childNode;
final String nodeName = child.getNodeName();
if ("bean".equals(nodeName)) {
String type = child.getAttribute("type");
String name = child.getAttribute("name");
String impl = child.getAttribute("class");
String onlyStatic = child.getAttribute("static");
String scopeStr = child.getAttribute("scope");
boolean optional = "true".equals(child.getAttribute("optional"));
.....部分代码省略
try {
Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
Class ctype = cimpl;
if (StringUtils.isNotEmpty(type)) {
ctype = ClassLoaderUtil.loadClass(type, getClass());
}
.....部分代码省略
cimpl.getDeclaredConstructors();
containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope);
}
loadedBeans.put(ctype.getName() + name, child);
} catch (Throwable ex) {
}
} else if ("constant".equals(nodeName)) {
String name = child.getAttribute("name");
String value = child.getAttribute("value");
props.setProperty(name, value, childNode);
}
}
}
}
}
}
这里的init方法我们看到通过文件名loadDocuments();
private void loadDocuments(String configFileName) {
try {
loadedFileUrls.clear();
documents = loadConfigurationFiles(configFileName, null);
} catch (ConfigurationException e) {
throw e;
} catch (Exception e) {
throw new ConfigurationException("Error loading configuration file " + configFileName, e);
}
}
看一下loadConfigurationFiles()这个方法
private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
....部分代码省略
docs.add(DomHelper.parse(in, dtdMappings));
}
查看一下parse方法,发现用SAX方式解析xml,返回document对象的集合。
现在在来看一下,XmlConfigurationProvider的register方法,里面获取constant和bean开始的标签。
现在再看一下,StrutsXmlConfigurationProvider的register方法,init方法继承XmlConfigurationProvider中的init方法。
public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
if (servletContext != null && !containerBuilder.contains(ServletContext.class)) {
containerBuilder.factory(ServletContext.class, new Factory<ServletContext>() {
public ServletContext create(Context context) throws Exception {
return servletContext;
}
});
}
super.register(containerBuilder, props);
}
这里的servletContext就是servlet的容器servletContext
下面,我们回到struts2过滤器的init方法中,发现有这个方法init.initStaticContentLoader(config, dispatcher)查看他的源码
public StaticContentLoader initStaticContentLoader( HostConfig filterConfig, Dispatcher dispatcher ) {
StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
loader.setHostConfig(filterConfig);
return loader;
}
看一下dispatcher.getContainer()这个方法,
public Container getContainer() {
if (ContainerHolder.get() != null) {
return ContainerHolder.get();
}
ConfigurationManager mgr = getConfigurationManager();
if (mgr == null) {
throw new IllegalStateException("The configuration manager shouldn't be null");
} else {
Configuration config = mgr.getConfiguration();
if (config == null) {
throw new IllegalStateException("Unable to load configuration");
} else {
Container container = config.getContainer();
ContainerHolder.store(container);
return container;
}
}
}
这里获取ConfigurationManager对象,前面我们在dispatcher对象init的时候,创建了一个ConfigurationManager对象,然后又调用到了 mgr.getConfiguration()方法,查看一下
public synchronized Configuration getConfiguration() {
if (configuration == null) {
setConfiguration(createConfiguration(defaultFrameworkBeanName));
try {
configuration.reloadContainer(getContainerProviders());
} catch (ConfigurationException e) {
setConfiguration(null);
throw new ConfigurationException("Unable to load configuration.", e);
}
} else {
conditionalReload();
}
return configuration;
}
这里我们看到了configuration.reloadContainer(getContainerProviders());这个方法
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();
Container bootstrap = createBootstrapContainer(providers);
for (final ContainerProvider containerProvider : providers)
{
bootstrap.inject(containerProvider);
containerProvider.init(this);
containerProvider.register(builder, props);
}
props.setConstants(builder);
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
setContext(bootstrap);
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);
((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);
}
rebuildRuntimeConfiguration();
} finally {
if (oldContext == null) {
ActionContext.setContext(null);
}
}
return packageProviders;
}
看到这两条containerProvider.init(this); containerProvider.register(builder, props);这里的containerProvider就是我们一开始dispatcher对象init方法中创建好的ContainerProvider。
这里就执行了init方法中register方法创建bean,constant常量。现在看一下 provider.loadPackages();这个方法。
public void loadPackages() throws ConfigurationException {
List<Element> reloads = new ArrayList<Element>();
verifyPackageStructure();
for (Document doc : documents) {
Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for (int i = 0; i < childSize; i++) {
Node childNode = children.item(i);
if (childNode instanceof Element) {
Element child = (Element) childNode;
final String nodeName = child.getNodeName();
if ("package".equals(nodeName)) {
PackageConfig cfg = addPackage(child);
if (cfg.isNeedsRefresh()) {
reloads.add(child);
}
}
}
}
loadExtraConfiguration(doc);
}
if (reloads.size() > 0) {
reloadRequiredPackages(reloads);
}
for (Document doc : documents) {
loadExtraConfiguration(doc);
}
documents.clear();
declaredPackages.clear();
configuration = null;
}
这里看到 PackageConfig cfg = addPackage(child);这个代码
protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
...部分代码省略
PackageConfig.Builder newPackage = buildPackageContext(packageElement);
// add result types (and default result) to this package
addResultTypes(newPackage, packageElement);
// load the interceptors and interceptor stacks for this package
loadInterceptors(newPackage, packageElement);
// load the default interceptor reference for this package
loadDefaultInterceptorRef(newPackage, packageElement);
// load the default class ref for this package
loadDefaultClassRef(newPackage, packageElement);
// load the global result list for this package
loadGlobalResults(newPackage, packageElement);
// load the global exception handler list for this package
loadGobalExceptionMappings(newPackage, packageElement);
// get actions
NodeList actionList = packageElement.getElementsByTagName("action");
for (int i = 0; i < actionList.getLength(); i++) {
Element actionElement = (Element) actionList.item(i);
addAction(actionElement, newPackage);
}
// load the default action reference for this package
loadDefaultActionRef(newPackage, packageElement);
PackageConfig cfg = newPackage.build();
configuration.addPackageConfig(cfg.getName(), cfg);
return cfg;
}
在这里加载了xml文件中配置的内容。
到此,struts2的配置加载完毕!!!