章節簡言 |
上一章筆者講到關於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中起到了重要的中間調度的作用。