配置管理之PackageProvider接口
| ?PackageProvider的開始 |
從前面幾章中我們了解到了一點:想知道如何加載相關配置文件就必須去找StrutsXmlConfigurationProvider類和XmlConfigurationProvider類。而StrutsXmlConfigurationProvider類和XmlConfigurationProvider類是在Dispatcher類的init_TraditionalXmlConfigurations方法里面被調用。代碼如下。
Dispatcher類:
1 private void init_TraditionalXmlConfigurations() {2 String configPaths = initParams.get("config");3 if (configPaths == null) {4 configPaths = DEFAULT_CONFIGURATION_PATHS;5 }6 String[] files = configPaths.split("\\s*[,]\\s*");7 for (String file : files) {8 if (file.endsWith(".xml")) {9 if ("xwork.xml".equals(file)) { 10 configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false)); 11 } else { 12 configurationManager 13 .addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext)); 14 } 15 } else { 16 throw new IllegalArgumentException("Invalid configuration file name"); 17 } 18 } 19 }看了上面的代碼。相信讀者也明白struts2會先去找過濾參數(initParams)里面是否有指定要去加載哪些配置文件。如果沒有的話,就用DEFAULT_CONFIGURATION_PATHS常量的值來加載。即是用"struts-default.xml,struts-plugin.xml,struts.xml"來解析加載??礃幼硬挥霉P者多講大家都明白了。加載相關配置文件的代碼其實就在這里開始發生的。然后就是進行供應者注冊的工作。(相關的內容在《Struts2 源碼分析——配置管理之ContainerProvider接口》也有講到) 這里筆者想提一下上面提到的struts-plugin.xml配置文件。這個置配文件是在插件包里面。如struts2-convention-plugin-2.5.2.jar等。也就是說XmlConfigurationProvider類也有加載插件包的配置信息功能。這一點在XmlConfigurationProvider類的loadConfigurationFiles方法里面體現的很明顯。而loadConfigurationFiles方法就是用于初始化時候,加載對應的配置文件??匆幌麓a吧。
XmlConfigurationProvider類:
1 public void init(Configuration configuration) { 2 this.configuration = configuration; 3 this.includedFileNames = configuration.getLoadedFileNames(); 4 loadDocuments(configFileName); 5 }XmlConfigurationProvider類 :
1 private void loadDocuments(String configFileName) {2 try {3 loadedFileUrls.clear();4 documents = loadConfigurationFiles(configFileName, null);5 } catch (ConfigurationException e) {6 throw e;7 } catch (Exception e) {8 throw new ConfigurationException("Error loading configuration file " + configFileName, e);9 } 10 }XmlConfigurationProvider類的loadConfigurationFiles方法:
1 Iterator<URL> urls = null;2 InputStream is = null;3 4 IOException ioException = null;5 try {6 urls = getConfigurationUrls(fileName);//獲得配置文件所以在的URLS。就是找到哪里包里面有fileName值的URLS7 } catch (IOException ex) {8 ioException = ex;9 } 10 11 if (urls == null || !urls.hasNext()) { 12 if (errorIfMissing) { 13 throw new ConfigurationException("Could not open files of the name " + fileName, ioException); 14 } else { 15 LOG.trace("Unable to locate configuration files of the name {}, skipping", fileName); 16 return docs; 17 } 18 }筆者沒有把關于loadConfigurationFiles方法的代碼他全部貼出來。只是貼出一部分。主要是想讓讀者知道。加載插件包的配置文件是如何進行的。為了什么要講這個呢?reloadContainer方法里面在加載package元素的時候,進行了倆個部分的加載工作。一分部是加載當前提供的供應者。另一部分就是加載插件包里面的供應者。所以就必須知道原來還有插件包里面的供應者。代碼如下:
1 ActionContext oldContext = ActionContext.getContext();2 try {3 4 setContext(bootstrap);//創建一個Action上下文5 container = builder.create(false);//新建一個Container容器6 setContext(container);//創建一個Action上下文7 objectFactory = container.getInstance(ObjectFactory.class);8 9 // 處理用戶配置里面的供應者,如果是PackageProvider,就是加載對應的package元素信息 10 for (final ContainerProvider containerProvider : providers) 11 { 12 if (containerProvider instanceof PackageProvider) { 13 container.inject(containerProvider); 14 ((PackageProvider)containerProvider).loadPackages(); 15 packageProviders.add((PackageProvider)containerProvider); 16 } 17 } 18 19 // 然后處理當前插件供應者,加載對應的package元素信息 20 Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class); 21 for (String name : packageProviderNames) { 22 PackageProvider provider = container.getInstance(PackageProvider.class, name); 23 provider.init(this); 24 provider.loadPackages(); 25 packageProviders.add(provider); 26 } 27 28 rebuildRuntimeConfiguration();//新建運行時候,用的配置 29 } finally { 30 if (oldContext == null) { 31 ActionContext.setContext(null); 32 } 33 }| PackageProvider的內容 |
相信到這里,大家都知道加載package元素在哪里開始執行的。而關于加載package元素中卻用到很多東西。讓筆者一個個講給大家聽吧。首先讓我們一下XmlConfigurationProvider類的loadPackages方法吧。這里才是正真加載工作。代碼如下?
XmlConfigurationProvider類:
1 public void loadPackages() throws ConfigurationException {2 List<Element> reloads = new ArrayList<Element>();3 verifyPackageStructure();4 5 for (Document doc : documents) {6 Element rootElement = doc.getDocumentElement();7 NodeList children = rootElement.getChildNodes();8 int childSize = children.getLength();9 10 for (int i = 0; i < childSize; i++) { 11 Node childNode = children.item(i); 12 13 if (childNode instanceof Element) { 14 Element child = (Element) childNode; 15 16 final String nodeName = child.getNodeName(); 17 18 if ("package".equals(nodeName)) {//判斷是否是package元素。 19 PackageConfig cfg = addPackage(child);//如果是增加package元素 20 if (cfg.isNeedsRefresh()) {//判斷是否需要重新加載 21 reloads.add(child); 22 } 23 } 24 } 25 } 26 loadExtraConfiguration(doc); 27 } 28 29 if (reloads.size() > 0) { 30 reloadRequiredPackages(reloads); 31 } 32 33 for (Document doc : documents) { 34 loadExtraConfiguration(doc); 35 } 36 37 documents.clear(); 38 declaredPackages.clear(); 39 configuration = null; 40 }看到了上面的代碼,大家都知道相關增加package元素的工作在addPackage方法里面進行的。而方法最后會返回一個PackageConfig類。PackageConfig類就是用于存放package元素信息的。為了方便讀者學習,筆者希望讀者能了解一下struts-2.5.dtd這個文件。筆者現在不清楚有多少人了解過DTD的相關語法?;蛟S很多人不知道DTD是什么東東。那么為什么要了解這個DTD文件呢?讓我們看一下DTD文件里面的一段代碼吧。
<!ELEMENT package (result-types?, interceptors?, default-interceptor-ref?, default-action-ref?, default-class-ref?, global-results?, global-allowed-methods?, global-exception-mappings?, action*)> <!ATTLIST packagename CDATA #REQUIREDextends CDATA #IMPLIEDnamespace CDATA #IMPLIEDabstract CDATA #IMPLIEDstrict-method-invocation (true|false) "true" >從上面的DTD信息我們很快了解到package元素節點到底有些什么內容。同時了解到package元素有哪里子節點。通過上面的信息在和PackageConfig類的成員變量進行對比學習的話,就比較清楚的知道為什么會有這個成員變量了。所以讓我們看一段關于PackageConfig類的代碼。如下
1 protected Map<String, ActionConfig> actionConfigs;//action的配置信息2 protected Map<String, ResultConfig> globalResultConfigs;//結果的配置信息3 protected Set<String> globalAllowedMethods;//公共允許的方法4 protected Map<String, Object> interceptorConfigs;//攔截器5 protected Map<String, ResultTypeConfig> resultTypeConfigs;//結果類型的配置信息6 protected List<ExceptionMappingConfig> globalExceptionMappingConfigs;//異常的配置信息7 protected List<PackageConfig> parents;//package元素的父配置信息8 protected String defaultInterceptorRef;//默認的攔截器9 protected String defaultActionRef;//默認的action 10 protected String defaultResultType;//默認的result信息 11 protected String defaultClassRef; 12 protected String name;//名字 13 protected String namespace = "";//命名空間 14 protected boolean isAbstract = false;//是否為抽象 15 protected boolean needsRefresh;//需要重新刷新 16 protected boolean strictMethodInvocation = true;讓筆者簡單的講解一下關于每個變量的作用吧。如下
1.Map<String, ActionConfig> actionConfigs:用于存放action的配置信息。我們都知道一個package可以對應多的action配置。
2.Map<String, ResultConfig> globalResultConfigs:用于存入對應的公共結果。也許有一種情況,那就是多個action共用一個結果。
3.Set<String> globalAllowedMethods:就是action允許被調用的方法。在struts-default.xml配置文件里面設置默認的值:execute,input,back,cancel,browse,save,delete,list,index。
4.Map<String, Object> interceptorConfigs:用于存放當前package元素的攔截器。對于攔截器的概念的話。后面的章節會講到。
5.Map<String, ResultTypeConfig> resultTypeConfigs:用于存放action返回的結果類型。
6.List<ExceptionMappingConfig> globalExceptionMappingConfigs:用于存放action發生異常的異常配置。
7.ist<PackageConfig> parents:用于存放當前package元素的父package元素的信息。
8.String defaultActionRef:標示當前package元素的默認action。
9.String defaultResultType:標示當前action返回的默認結果類型。
10.String defaultClassRef:action類默認的父類。
11.String name:package元素的名稱
12.String namespace :package元素的命名空間
13.boolean isAbstract:package元素是否為抽象
14.boolean needsRefresh:標示是否需要重新刷新。
15.boolean strictMethodInvocation:標示是否啟動SMI.關于SMI請找相關的知識點。
好了。理解了PackageConfig類的信息之后,讓我們看一下addPackage方法代碼吧。
1 protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {2 String packageName = packageElement.getAttribute("name");3 PackageConfig packageConfig = configuration.getPackageConfig(packageName);4 if (packageConfig != null) {5 LOG.debug("Package [{}] already loaded, skipping re-loading it and using existing PackageConfig [{}]", packageName, packageConfig);6 return packageConfig;7 }8 9 PackageConfig.Builder newPackage = buildPackageContext(packageElement); 10 11 if (newPackage.isNeedsRefresh()) { 12 return newPackage.build(); 13 } 14 15 LOG.debug("Loaded {}", newPackage); 16 17 // 增加結果類型到newPackage里面去。 18 addResultTypes(newPackage, packageElement); 19 20 // 增加攔截器和攔截棧到newPackage里面去。 21 loadInterceptors(newPackage, packageElement); 22 23 // 設置newPackage的默認攔截器 24 loadDefaultInterceptorRef(newPackage, packageElement); 25 26 // 設置newPackage的默認類,即是action類的父類 27 loadDefaultClassRef(newPackage, packageElement); 28 29 // 增加公共結果到newPackage里面去。 30 loadGlobalResults(newPackage, packageElement); 31 //設置允許的方法 32 loadGlobalAllowedMethods(newPackage, packageElement); 33 34 // 增加異常處理newPackage里面去。 35 loadGlobalExceptionMappings(newPackage, packageElement); 36 37 // 加載對應的action信息。并增加到newPackage里面去。 38 NodeList actionList = packageElement.getElementsByTagName("action"); 39 40 for (int i = 0; i < actionList.getLength(); i++) { 41 Element actionElement = (Element) actionList.item(i); 42 addAction(actionElement, newPackage); 43 } 44 45 // 設置newPackage默認的ACTION 46 loadDefaultActionRef(newPackage, packageElement); 47 48 PackageConfig cfg = newPackage.build(); 49 configuration.addPackageConfig(cfg.getName(), cfg);//增加到配置類里面 50 return cfg; 51 }從上面的代碼中我們可以發現最后獲得package元素信息都會增加Configuration接口對應的實例。即是DefaultConfiguration類的實例。這個方法也面也調用了很多方法來完成增加package元素。這些方法筆者并不想講解。請讀者自行根據筆者對方法的定義去查看源碼。而這里面有一點到是值得筆者注意的。那便是在PackageConfig類的實例的時候,好像用到建造者模式來實現。所以讀者在查看源碼的時候,如果不懂為什么作者要這樣子寫的話。請自行去查看相關的建造者模式的知識點。而加載package元素信息的工作到這里就算是結束了。
在加載package元素信息的工作結束之后,還有一件工作也是值得注意的。那便是上面reloadContainer方法代碼中出現的rebuildRuntimeConfiguration方法。這個方法做了什么呢?在筆者理解為創建一個運行時的配置信息,用于方便調用。在什么時候調用呢?至少筆者在DefaultActionProxy類的prepare方法調用到了。這個prepare方法是在action請求執行action將用到。詳細的內容筆者會在后面的章節里面講到。rebuildRuntimeConfiguration方法最后會創建一個叫RuntimeConfiguration接口的實例,即是RuntimeConfigurationImpl類的實例。
| 本章總結 |
本章的重點是知道struts2是如何加載相關的package元素節點信息的。那為什么要知道這部分的內容。相信筆者心里面應該筆者更清楚。如果不知道package元素的信息。那么struts2如何根據用戶輸入的URL來處理和運行相關的action類呢?不知道筆者是否還記得核心機制的圖片。可以這么說吧。到這一章相關橙黃色(Servlet Filters)部分的知識可以都結束了。我們知道如何加載相關的配置信息,知道如何加載package元素信息。而下一章筆者將對藍色(Struts core)部分的知識進行講解。即是根據現有的配置信息來處理用戶發來的action請求。
轉載于:https://www.cnblogs.com/chenliyang/p/6552575.html
總結
以上是生活随笔為你收集整理的配置管理之PackageProvider接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SharedActivityContex
- 下一篇: Screened Poisson Sur