分析
要想弄清struts2的配置文件加载顺序问题,首先我们必须要知道struts2的入口在什么地方?
02 | <filter-name>struts2</filter-name> |
04 | org.apache.struts2.dispatcher.FilterDispatcher |
08 | <filter-name>struts2</filter-name> |
09 | <url-pattern>/*</url-pattern> |
从上述web.xml的filter节中,我们可以看到struts2是在filter中定义了一个FilterDispatcher,用来拦截符合在filter-mapping中定义的url-pattern的所有url请求,然后将拦截到的url请求交给该FilterDispather处理,所以接下来我们将重点分析该filter,既然是一个过滤器,那么最重要的方法莫过于三个,分别是init(),doFilter(),destroy(),从三个方法的名称结合我们本次源码分析的任务,我们将重点分析init()方法,顾名思义,该方法进行struts2的初始化工作。
经过上述的简单思考,我们接下来将进行具体的源码分析工作。
源码分析
和之前的源码分析工作一样,首先还是先下载struts2源码(版本为struts-2.3.12),然后将其导入eclipse,方便查看分析。待一切准备工作就绪后,我们进行具体的分析工作。
02 | * Initializes the filter by creating a default dispatcher |
03 | * and setting the default packages for static resources. |
05 | * @param filterConfig The filter configuration |
07 | public void init(FilterConfig filterConfig) throws ServletException { |
09 | this .filterConfig = filterConfig; |
13 | dispatcher = createDispatcher(filterConfig); |
15 | dispatcher.getContainer().inject( this ); |
17 | staticResourceLoader.setHostConfig( new FilterHostConfig(filterConfig)); |
19 | ActionContext.setContext( null ); |
从init()函数上面的注释可以看到,该方法是通过创建一个默认的dispatcher和设置默认的静态资源包来初始化该过滤器。
从上述具体的处理流程我们可以看到,所有的初始化工作,应该都是在dispachter.init()方法中,所以接下来将重点分析该方法。
02 | * Load configurations, including both XML and zero-configuration strategies, |
03 | * and update optional settings, including whether to reload configurations and resource files. |
07 | if (configurationManager == null ) { |
08 | configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); |
13 | init_DefaultProperties(); |
14 | init_TraditionalXmlConfigurations(); |
15 | init_LegacyStrutsProperties(); |
16 | init_CustomConfigurationProviders(); |
17 | init_FilterInitParameters() ; |
18 | init_AliasStandardObjects() ; |
20 | Container container = init_PreloadConfiguration(); |
21 | container.inject( this ); |
22 | init_CheckWebLogicWorkaround(container); |
24 | if (!dispatcherListeners.isEmpty()) { |
25 | for (DispatcherListener l : dispatcherListeners) { |
26 | l.dispatcherInitialized( this ); |
29 | } catch (Exception ex) { |
30 | if (LOG.isErrorEnabled()) |
31 | LOG.error( "Dispatcher initialization failed" , ex); |
32 | throw new StrutsException(ex); |
从该函数的注释头部分我们可以看到,该方法是加载配置信息并更新可选择的配置(某些配置信息,虽然在前面已经配置了,但还是可以在后面的配置文件中对其进行覆盖操作)。从上述七个init_*函数,与我们本次源码分析目标相关的函数应该是init_DefaultProperties()、init_TraditionalXmlConfigurations()以及init_LegacyStrutsProperties(),接下来我们将一个个的加以分析:
首先是init_DefaultProperties(),该函数定义如下:
1 | private void init_DefaultProperties() { |
2 | configurationManager.addContainerProvider( new DefaultPropertiesProvider()); |
02 | * Loads the default properties, separate from the usual struts.properties loading |
04 | public class DefaultPropertiesProvider extends LegacyPropertiesConfigurationProvider { |
06 | public void destroy() { |
09 | public void init(Configuration configuration) throws ConfigurationException { |
12 | public void register(ContainerBuilder builder, LocatableProperties props) |
13 | throws ConfigurationException { |
15 | Settings defaultSettings = null ; |
17 | defaultSettings = new PropertiesSettings( "org/apache/struts2/default" ); |
18 | } catch (Exception e) { |
19 | throw new ConfigurationException( "Could not find or error in org/apache/struts2/default.properties" , e); |
22 | loadSettings(props, defaultSettings); |
以上可以看到,其处理的配置文件是:org/apache/struts2/default.properties
其次是init_TraditionalXmlConfigurations(),该函数定义如下:
01 | private void init_TraditionalXmlConfigurations() { |
02 | String configPaths = initParams.get( "config" ); |
03 | if (configPaths == null ) { |
04 | configPaths = DEFAULT_CONFIGURATION_PATHS; |
06 | String[] files = configPaths.split( "\\s*[,]\\s*" ); |
07 | for (String file : files) { |
08 | if (file.endsWith( ".xml" )) { |
09 | if ( "xwork.xml" .equals(file)) { |
10 | configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false )); |
12 | configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false , servletContext)); |
15 | throw new IllegalArgumentException( "Invalid configuration file name" ); |
2 | * Provide list of default configuration files. |
4 | private static final String DEFAULT_CONFIGURATION_PATHS = "struts-default.xml,struts-plugin.xml,struts.xml" ; |
上述函数的处理流程是:
如果configPaths为Null,则使用其默认值
1 | "struts-default.xml,struts-plugin.xml,struts.xml" |
然后根据[,]进行分割,得到三个文件名:struts-default.xml, struts-plugin.xml, struts.xml,并以此对这三个文件进行处理,如果文件名以*.xml结尾且不是xwork.xml,则调用函数
1 | configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false , servletContext)); |
从上述可以看到,所谓的struts-*.xml配置文件处理顺序,其实就是变量DEFAULT_CONFIGURATION_PATHS中,字符串定义的顺序。
最后一个函数是init_LegacyStrutsProperties(),从函数名,我们可以简单的判断出,该函数是处理遗留下来的struts配置文件,其定义如下:
1 | private void init_LegacyStrutsProperties() { |
2 | configurationManager.addContainerProvider( new LegacyPropertiesConfigurationProvider()); |
02 | builder.factory(Locale. class , new Factory() { |
03 | private Locale locale; |
05 | public synchronized Object create(Context context) throws Exception { |
07 | String loc = context.getContainer().getInstance(String. class , StrutsConstants.STRUTS_LOCALE); |
09 | StringTokenizer localeTokens = new StringTokenizer(loc, "_" ); |
11 | String country = null ; |
13 | if (localeTokens.hasMoreTokens()) { |
14 | lang = localeTokens.nextToken(); |
17 | if (localeTokens.hasMoreTokens()) { |
18 | country = localeTokens.nextToken(); |
20 | locale = new Locale(lang, country); |
22 | if (LOG.isInfoEnabled()) { |
23 | LOG.info( "No locale define, substituting the default VM locale" ); |
25 | locale = Locale.getDefault(); |
1 | /** The default locale for the Struts application */ |
2 | public static final String STRUTS_LOCALE = "struts.locale" ; |
从上面的处理流程,我们可以看到,主要是加载配置文件struts.locale文件。
总结
从上述的源码分析,我们可以看到,struts2在处理配置文件的一个相对顺序为:
default.properties -> struts-default.xml -> struts-plugins.xml -> struts.xml -> struts.locale
请注意上述描述用词,是相对顺序,还有很多配置文件未列入,如果想了解更多的信息,可继续分析上述提到的init_*函数。