Struts2 源碼分析——調結者(Dispatcher)之准備工作


章節簡言

上一章筆者講到關於struts2過濾器(Filter)的知識。讓我們了解到StrutsPrepareFilter和StrutsExecuteFilter的作用。特別是StrutsPrepareFilter做了重要的講解。從其中我們了解到Dispatcher類的重要性。而本章就是專對Dispatcher類的工作進行講解。從前面章節的機制圖片中我們橙黃色區里面看到FilterDispatcher。在筆者理解這里的FilterDispatcher相當於Dispatcher類的工作。那么到底Dispatcher類做了哪一些的工作呢?本章就是筆者就會詳細的進行講解。那么在講解之前筆者還是有想把一些必要的知識說一下。從上一章中我們可以明白StrutsPrepareFilter類的主要工作有倆點:一是為struts2執行做一些相關的准備。如加載相關的配置信息。二是為struts2的request請求處理相關的信息。如設置編碼格式和找到對應的action映射類。而這二點都離不開Dispatcher類的作用。甚至可以講大部分都要靠Dispatcher類來完成。筆者很想把Dispatcher類的源碼全部都POST上來。可是想到這樣子讀起來有一點吃力。所以筆者打算將來部分部分的POST上來進行講解。

調結者的准備工作

在執行struts2之前必然要加載一些相關信息。如配置文件struts.xml之類。沒有錯。StrutsPrepareFilter就是通過Dispatcher類來完成這一系列的工作的(下面代碼的紅色部分)。讓我們看一下Dispatcher類的代碼就是能夠明白。如下

StrutsPrepareFilte類:

 1  public void init(FilterConfig filterConfig) throws ServletException {
 2         InitOperations init = new InitOperations();//用於初始化相關的功能操作。你可以理解為工具類一樣子。
 3         Dispatcher dispatcher = null;//這個類相當的重要。他的作用連接着StrutsExecuteFilter。這里可以命名為調結者。
 4         try {
 5             FilterHostConfig config = new FilterHostConfig(filterConfig);//這里可以理解為把filterConfig在進行封裝FilterHostConfig更為主便操作和理解。
 6             init.initLogging(config);//獲取名為loggerFactory的參數,並實例化這個類。一般為去用戶自定義日志。
 7             dispatcher = init.initDispatcher(config);//初化調結者。這里是重要。
 8 
 9             prepare = new PrepareOperations(dispatcher);
10             this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);//加載排除在內的action的正則表達式
11 
12             postInit(dispatcher, filterConfig);
13         } finally {
14             if (dispatcher != null) {
15                 dispatcher.cleanUpAfterInit();
16             }
17             init.cleanup();
18         }
19     }

 

Dispatcher類:

 1 public void init() {
 2 
 3         if (configurationManager == null) {
 4             configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);// 初始化配置管理類
 5         }
 6 
 7         try {
 8             init_FileManager();// 初始化FileManager類的供應者
 9             init_DefaultProperties(); // [1]初始化default.properties信息的供應者
10             init_TraditionalXmlConfigurations(); // [2]初始化struts-default.xml,struts-plugin.xml,struts.xml信息的供應者
11             init_LegacyStrutsProperties(); // [3]初始化struts.properties信息的供應者
12                                             // 可能是以前版本留下的文件。在這里筆者一直沒有找到
13             init_CustomConfigurationProviders(); // [5]初始化用戶struts.xml信息的供應者
14             init_FilterInitParameters(); // [6]初始化用戶傳入的過濾器參數信息的供應者
15             init_AliasStandardObjects(); // [7]初始化struts-default.xml,struts-plugin.xml,struts.xml進行別名信息的供應者
16 
17             Container container = init_PreloadConfiguration();// 初始化相關的配置信息,並加載以上供應者。
18             container.inject(this);// 注入當前類依賴項的信息。
19             init_CheckWebLogicWorkaround(container);
20 
21             if (!dispatcherListeners.isEmpty()) {
22                 for (DispatcherListener l : dispatcherListeners) {
23                     l.dispatcherInitialized(this);
24                 }
25             }
26             errorHandler.init(servletContext);
27 
28         } catch (Exception ex) {
29             LOG.error("Dispatcher initialization failed", ex);
30             throw new StrutsException(ex);
31         }
32     }

以上的源碼就是Dispatcher類加載相關配置信息工作的代碼。從上面的注解中我們可以看“供應者”這三個字樣。為什么這邊筆者要叫他們為供應者。主要是struts2這邊用到了一個概念就是IOC思想。即是控制反轉(Inversion of Control)。簡單點理解IOC就是有一個容器,里面有很多要用到的實例或是類的信息。當開發員要用到某個類的實例的時候,不在是NEW了。而是通過當前容器來獲得實例。即是有一點類似於工廠模式。如果還是不能理解的話,請讀者自行去找一些相關IOC思想的文章。筆者這里不會細說IOC思想。 Container類的實例就是筆者所講的容器。而前面的供應者是為Container容器提供對應的類的信息或實例。確切的講Container容器創建的時候,就會去找所有供應者並讓供應者提供數據。值得一提的是這里面還有澀及到一個重要的中間人配置管理類(ConfigurationManager)。讓我們看一下下面的代碼,就是知道是什么一回事了。如下

/**
     * 初始化文件管理器供應者
     */
    private void init_FileManager() throws ClassNotFoundException {
        if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER)) {
            final String fileManagerClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER);
            final Class<FileManager> fileManagerClass = (Class<FileManager>) Class.forName(fileManagerClassName);
            LOG.info("Custom FileManager specified: {}", fileManagerClassName);
           configurationManager .addContainerProvider(new FileManagerProvider(fileManagerClass, fileManagerClass.getSimpleName()));
        } else {
            // add any other Struts 2 provided implementations of FileManager
            configurationManager.addContainerProvider(new FileManagerProvider(JBossFileManager.class, "jboss"));
        }
        if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY)) {
            final String fileManagerFactoryClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY);
            final Class<FileManagerFactory> fileManagerFactoryClass = (Class<FileManagerFactory>) Class
                    .forName(fileManagerFactoryClassName);
            LOG.info("Custom FileManagerFactory specified: {}", fileManagerFactoryClassName);
            configurationManager.addContainerProvider(new FileManagerFactoryProvider(fileManagerFactoryClass));
        }
    }

    /**
     * 初始化加載default.properties文件信息的供應者
     */
    private void init_DefaultProperties() {
        configurationManager.addContainerProvider(new DefaultPropertiesProvider());
    }

    private void init_LegacyStrutsProperties() {
        configurationManager.addContainerProvider(new PropertiesConfigurationProvider());
    }

    /**
     * 初始化struts-default.xml,struts-plugin.xml,struts.xml信息的供應者
     */
    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");
            }
        }
    }

看到上面的紅色代碼了嗎?沒有錯。從這一點我們就是看出來。所以初始化的操作都是先創建對應的供應者。並把供應者實列增加到ConfigurationManager實例中去。正如上面所講的供應者是為Container容器提供數據的。那么到底供應者又長什么樣子呢?這就不得不讓我們在看另一段代碼了。如下

 1 package com.opensymphony.xwork2.config;
 2 
 3 import com.opensymphony.xwork2.inject.ContainerBuilder;
 4 import com.opensymphony.xwork2.util.location.LocatableProperties;
 5 
 6 
 7 /**
 8  * 用於給Container容器提供對應的對象,常量和屬性的供應者類
 9  * 
10  * @since 2.1
11  */
12 public interface ContainerProvider {
13 
14     /**
15      * 消毀當前的供應者
16      */
17     public void destroy();
18     
19     /**
20      * 初始化供應者
21      * @param configuration The configuration
22      * @throws ConfigurationException If anything goes wrong
23      */
24     public void init(Configuration configuration) throws ConfigurationException;
25     
26     /**
27      * 是否需要重新加載
28      *
29      * @return <tt>true</tt>, whether the ContainerProvider should reload its configuration, <tt>false</tt>otherwise.
30      */
31     public boolean needsReload();
32     
33     /**
34      * 用於注入容器的方法
35      * 
36      * @param builder The builder to register beans with
37      * @param props The properties to register constants with
38      * @throws ConfigurationException If anything goes wrong
39      */
40     public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
41     
42 }

上面代碼里面的ContainerProvider就是所有供應者類的父類接口類之一。從上面代碼就是可以看出來他的作用就是為Container容器服務的。其中register方法就是最明顯的代表了。即是把信息注入到Container容器。好了。讓筆者好好整理一下。struts2執行之前都做了一些什么工作。即是StrutsPrepareFilter類在Dispatcher類的幫助下做了些什么。如下。

1.判斷ConfigurationManager類是否存在,如果不存在就是創建。

2.初始化文件管理類的供應者,並增加ConfigurationManager實例中。用於監督和管理加載的文件。

3.初始化加載default.properties文件信息的供應者,並增加ConfigurationManager實例中。用於加載struts2包default.properties信息。

4.初始化struts-default.xml,struts-plugin.xml,struts.xml信息的供應者,並增加ConfigurationManager實例中。用於加載struts2包中的struts-default.xml,struts-plugin.xml,struts.xml信息。

5.初始化struts.properties信息的供應者,並增加ConfigurationManager實例中。這里筆者也有一點奇怪。源碼里面是去加載struts.properties信息。可是筆者一直沒有找到對應的文件。

6.初始化用戶傳入的過濾器參數信息的供應者,並增加ConfigurationManager實例中。即是把過濾器參數一塊注入到Container容器里面。

7.初始化struts-default.xml,struts-plugin.xml,struts.xml進行別名信息的供應者。用於加載struts2包中的struts-default.xml,struts-plugin.xml,struts.xml信息。只是這里用別名進行注入。

8.創建Container容器。把以上所有的供應者所提供的信息全部注入到Container容器。即是對象,常量等信息。

9.給Dispatcher類本身進行依賴注入。筆者相信讀者會看Dispatcher類的方法上面有幾個@Inject的關鍵字。沒有錯。這就是說明當前方法是用於依賴注入的。用Container類的inject方法就是注入的意思。

10.判斷是否存在Dispatcher監聽。如果存在就是執行。

11.初始化錯誤處理類。

本章總結

本章重點講的是struts2啟動的時候需要加載的相關工作。這些工作大部分是靠Dispatcher類來完成。所以StrutsPrepareFilter類的代碼中有Dispatcher類的出現。最后會作為PrepareOperations類的構造參數存放起來。以備request請求處理的需要做准備。下一章筆者將會講到。通過本章的講解我們就會明白Dispatcher類在整個strtus2中起到了重要的中間調度的作用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM