本次源码分析的目标:web
弄清struts2加载各配置文件的顺序,获得此配置文件加载顺序,则源码分析任务结束。算法
问题的引出是因为前些天在oschina上看到的一篇帖子,http://www.oschina.net/question/593078_105422,截图以下:apache
带着这样的一个问题,咱们尝试从struts2源码的角度去解答。编程
要想弄清struts2的配置文件加载顺序问题,首先咱们必需要知道struts2的入口在什么地方?app
<filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
从上述web.xml的filter节中,咱们能够看到struts2是在filter中定义了一个FilterDispatcher,用来拦截符合在filter-mapping中定义的url-pattern的全部url请求,而后将拦截到的url请求交给该FilterDispather处理,因此接下来咱们将重点分析该filter,既然是一个过滤器,那么最重要的方法莫过于三个,分别是init(),doFilter(),destroy(),从三个方法的名称结合咱们本次源码分析的任务,咱们将重点分析init()方法,顾名思义,该方法进行struts2的初始化工做。eclipse
通过上述的简单思考,咱们接下来将进行具体的源码分析工做。ide
和以前的源码分析工做同样,首先仍是先下载struts2源码(版本为struts-2.3.12),而后将其导入eclipse,方便查看分析。待一切准备工做就绪后,咱们进行具体的分析工做。函数
/** * Initializes the filter by creating a default dispatcher * and setting the default packages for static resources. * * @param filterConfig The filter configuration */ public void init(FilterConfig filterConfig) throws ServletException { try { this.filterConfig = filterConfig; initLogging(); dispatcher = createDispatcher(filterConfig); dispatcher.init(); dispatcher.getContainer().inject(this); staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig)); } finally { ActionContext.setContext(null); } }
从init()函数上面的注释能够看到,该方法是经过建立一个默认的dispatcher和设置默认的静态资源包来初始化该过滤器。源码分析
从上述具体的处理流程咱们能够看到,全部的初始化工做,应该都是在dispachter.init()方法中,因此接下来将重点分析该方法。ui
/** * 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(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); } }
从该函数的注释头部分咱们能够看到,该方法是加载配置信息并更新可选择的配置(某些配置信息,虽然在前面已经配置了,但仍是能够在后面的配置文件中对其进行覆盖操做)。从上述七个init_*函数,与咱们本次源码分析目标相关的函数应该是init_DefaultProperties()、init_TraditionalXmlConfigurations()以及init_LegacyStrutsProperties(),接下来咱们将一个个的加以分析:
首先是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 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); } }
以上能够看到,其处理的配置文件是:org/apache/struts2/default.properties
其次是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";
上述函数的处理流程是:
若是configPaths为Null,则使用其默认值
"struts-default.xml,struts-plugin.xml,struts.xml"
而后根据[,]进行分割,获得三个文件名:struts-default.xml, struts-plugin.xml, struts.xml,并以此对这三个文件进行处理,若是文件名以*.xml结尾且不是xwork.xml,则调用函数
configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
从上述能够看到,所谓的struts-*.xml配置文件处理顺序,其实就是变量DEFAULT_CONFIGURATION_PATHS中,字符串定义的顺序。
最后一个函数是init_LegacyStrutsProperties(),从函数名,咱们能够简单的判断出,该函数是处理遗留下来的struts配置文件,其定义以下:
private void init_LegacyStrutsProperties() { configurationManager.addContainerProvider(new LegacyPropertiesConfigurationProvider()); }
// Set default locale by lazily resolving the locale property as needed into a Locale object builder.factory(Locale.class, new Factory() { private Locale locale; public synchronized Object create(Context context) throws Exception { if (locale == null) { String loc = context.getContainer().getInstance(String.class, StrutsConstants.STRUTS_LOCALE); if (loc != null) { StringTokenizer localeTokens = new StringTokenizer(loc, "_"); String lang = null; String country = null; if (localeTokens.hasMoreTokens()) { lang = localeTokens.nextToken(); } if (localeTokens.hasMoreTokens()) { country = localeTokens.nextToken(); } locale = new Locale(lang, country); } else { if (LOG.isInfoEnabled()) { LOG.info("No locale define, substituting the default VM locale"); } locale = Locale.getDefault(); } } return locale; } });
/** The default locale for the Struts application */ public static final String STRUTS_LOCALE = "struts.locale";
从上面的处理流程,咱们能够看到,主要是加载配置文件struts.locale文件。
从上述的源码分析,咱们能够看到,struts2在处理配置文件的一个相对顺序为:
default.properties -> struts-default.xml -> struts-plugins.xml -> struts.xml -> struts.locale
请注意上述描述用词,是相对顺序,还有不少配置文件未列入,若是想了解更多的信息,可继续分析上述提到的init_*函数。
若是您对算法或编程感兴趣,欢迎扫描下方二维码并关注公众号“算法与编程之美”,和您一块儿探索算法和编程的神秘之处,给您不同的解题分析思路。