struts2源碼分析之配置文件加載順序

本次源碼分析的目標: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_*函數。 

若是您對算法或編程感興趣,歡迎掃描下方二維碼並關注公衆號「算法與編程之美」,和您一塊兒探索算法和編程的神祕之處,給您不同的解題分析思路。

相關文章
相關標籤/搜索