源码分析:Struts 2配置文件加载顺序
Struts 2可在多个配置文件设置属性值,而且可以多处配置同一常量,那么系统以什么方式加载,并以何处的值为准呢?
分析过程
这里从入口开始分析,逐渐按顺序找到各个配置文件。
1. 入口 Filter&Dispatcher
考虑到属性值应该是系统正常运行时就赋值好的,那么可能在系统初始化时就执行加载和赋值操作。Struts 2加载的入口类是:
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
这个 filter 的 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();
}
}
属性的初始化工作,通过代码字面的含义,可能是黑体部分 init.initDispatcher(config)
查看这个 InitOperations.initDispatcher(config)
方法,内容如下:
/**
* Creates and initializes the dispatcher
*/
public Dispatcher initDispatcher( HostConfig filterConfig ) {
Dispatcher dispatcher = createDispatcher(filterConfig);
dispatcher.init();
return dispatcher;
}
具体的初始化操作,再查看黑体部分的方法: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(); // [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);
}
}
看上面方法的注释,确实找对地方了。
2. default.properties
先看 init_DefaultProperties()
方法:
private void init_DefaultProperties() {
configurationManager.addContainerProvider(new 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);
}
}
}
可见首先加载的是 org/apache/struts2/default.properties
3. *.xml
再来看 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");
}
}
}
/**
* Provide list of default configuration files.
*/
private static final String DEFAULT_CONFIGURATION_PATHS = "struts-default.xml,struts-plugin.xml,struts.xml";
上面的代码显示,将从 web.xml 中读取 config 参数的值,如果有 xwork.xml 就读取 xwork.xml 的值——这可能是历史遗留的兼容问题所致,如果 config 值未设置,则读取默认值:
struts-default.xml,struts-plugin.xml,struts.xml
这三个配置文件,顺序自然是 DEFAULT_CONFIGURATION_PATHS
定义的顺序。
具体内容还可以自定义。
4. Legacy Properties
再来看 init_LegacyStrutsProperties()
这个方法:
private void init_LegacyStrutsProperties() {
configurationManager.addContainerProvider(new PropertiesConfigurationProvider());
}
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
final DefaultSettings settings = new DefaultSettings();
loadSettings(props, settings);
}
/**
* Constructs an instance by loading the standard property files,
* any custom property files (<code>struts.custom.properties</code>),
* and any custom message resources ().
* <p>
* Since this constructor combines Settings from multiple resources,
* it utilizes a {@link DelegatingSettings} instance,
* and all API calls are handled by that instance.
*/
public DefaultSettings() {
ArrayList<Settings> list = new ArrayList<Settings>();
// stuts.properties, default.properties
try {
list.add(new PropertiesSettings("struts"));
} catch (Exception e) {
LOG.warn("DefaultSettings: Could not find or error in struts.properties", e);
}
delegate = new DelegatingSettings(list);
// struts.custom.properties
String files = delegate.get(StrutsConstants.STRUTS_CUSTOM_PROPERTIES);
if (files != null) {
StringTokenizer customProperties = new StringTokenizer(files, ",");
while (customProperties.hasMoreTokens()) {
String name = customProperties.nextToken();
try {
list.add(new PropertiesSettings(name));
} catch (Exception e) {
LOG.error("DefaultSettings: Could not find " + name + ".properties. Skipping.");
}
}
delegate = new DelegatingSettings(list);
}
}
以上代码可以看出,这里加载的是 struts.properties
, 而且可以通过 StrutsConstants.STRUTS_CUSTOM_PROPERTIES
设置自定义的配置文件。
5. 结论
通过上面的分析,可以发现 Struts 2 对配置文件的加载顺序是:
default.properties -> struts-default.xml -> struts-plugin.xml -> struts.xml -> struts.properties
後加载的文件中的赋值,会覆盖前面的文件中的赋值。
但不知道为什么很多地方都说是这个顺序:
struts-default.xml -> struts-plugin.xml -> struts.xml -> struts.properties -> web.xml
配置文件说明
下面详细说明相关配置文件
1. default.properties
/org/apache/struts2/default.properties
这个文件位于 struts2-core-2.3.16.1.jar 中,是 Struts 2默认的配置文件,含有大量 Struts 2属性参数的默认值以及说明。
这里的配置一般不需修改。
另,org.apache.struts2.StrutsConstants
中也有常量定义说明。
2. struts-default.xml
默认位置:struts2-core-2.3.16.1.jar/struts-default.xml
这里的配置一般不需修改。
3. struts-plugin.xml
据称位于一些 struts 2 插件的 jar 包中,如:
struts2-tiles3-plugin-2.3.16.1.jar/struts-plugin.xml
struts2-gxp-plugin-2.3.16.1.jar/struts-plugin.xml
struts2-embeddedjsp-plugin-2.3.16.1.jar/struts-plugin.xml
struts2-osgi-plugin-2.3.16.1.jar/struts-plugin.xml
这些 struts-plugin.xml 中的内容不会冲突。
4. struts.xml
默认位置:{web app home}/WEB-INF/classes/struts/struts.xml
这是 Web app 默认的配置文件,可以通过修改 Web app 的 web.xml 来修改它的路径:
<filter>
<filter-name> struts2 </filter-name>
<filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class>
<init-param>
<param-name> config </param-name>
<param-value>struts-default.xml,struts-plugin.xml,META-INF/config/struts/struts.xml </param-value>
</init-param>
</filter>
5. struts.properties
默认位置:{web app home}/WEB-INF/classes/struts.properties
按当前的 Struts 2版本,这个已经算是遗留方式了,不建议考虑修改它了。
6. web.xml
默认位置:{web app home}/web.xml
web.xml 是 web 应用的配置文件,一般不通过这种方式配置常量,在上面的源码分析中,也没发现最後以此为准的说法 TODO
参考:
《struts2源码分析之配置文件加载顺序》:http://my.oschina.net/gschen/blog/121433