配置管理之PackageProvider接口
| ?PackageProvider的開始 |
從前面幾章中我們了解到了一點(diǎn):想知道如何加載相關(guān)配置文件就必須去找StrutsXmlConfigurationProvider類和XmlConfigurationProvider類。而StrutsXmlConfigurationProvider類和XmlConfigurationProvider類是在Dispatcher類的init_TraditionalXmlConfigurations方法里面被調(diào)用。代碼如下。
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會(huì)先去找過(guò)濾參數(shù)(initParams)里面是否有指定要去加載哪些配置文件。如果沒有的話,就用DEFAULT_CONFIGURATION_PATHS常量的值來(lái)加載。即是用"struts-default.xml,struts-plugin.xml,struts.xml"來(lái)解析加載。看樣子不用筆者多講大家都明白了。加載相關(guān)配置文件的代碼其實(shí)就在這里開始發(fā)生的。然后就是進(jìn)行供應(yīng)者注冊(cè)的工作。(相關(guān)的內(nèi)容在《Struts2 源碼分析——配置管理之ContainerProvider接口》也有講到) 這里筆者想提一下上面提到的struts-plugin.xml配置文件。這個(gè)置配文件是在插件包里面。如struts2-convention-plugin-2.5.2.jar等。也就是說(shuō)XmlConfigurationProvider類也有加載插件包的配置信息功能。這一點(diǎn)在XmlConfigurationProvider類的loadConfigurationFiles方法里面體現(xiàn)的很明顯。而loadConfigurationFiles方法就是用于初始化時(shí)候,加載對(duì)應(yīng)的配置文件。看一下代碼吧。
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 }筆者沒有把關(guān)于loadConfigurationFiles方法的代碼他全部貼出來(lái)。只是貼出一部分。主要是想讓讀者知道。加載插件包的配置文件是如何進(jìn)行的。為了什么要講這個(gè)呢?reloadContainer方法里面在加載package元素的時(shí)候,進(jìn)行了倆個(gè)部分的加載工作。一分部是加載當(dāng)前提供的供應(yīng)者。另一部分就是加載插件包里面的供應(yīng)者。所以就必須知道原來(lái)還有插件包里面的供應(yīng)者。代碼如下:
1 ActionContext oldContext = ActionContext.getContext();2 try {3 4 setContext(bootstrap);//創(chuàng)建一個(gè)Action上下文5 container = builder.create(false);//新建一個(gè)Container容器6 setContext(container);//創(chuàng)建一個(gè)Action上下文7 objectFactory = container.getInstance(ObjectFactory.class);8 9 // 處理用戶配置里面的供應(yīng)者,如果是PackageProvider,就是加載對(duì)應(yīng)的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 // 然后處理當(dāng)前插件供應(yīng)者,加載對(duì)應(yīng)的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();//新建運(yùn)行時(shí)候,用的配置 29 } finally { 30 if (oldContext == null) { 31 ActionContext.setContext(null); 32 } 33 }| PackageProvider的內(nèi)容 |
相信到這里,大家都知道加載package元素在哪里開始執(zhí)行的。而關(guān)于加載package元素中卻用到很多東西。讓筆者一個(gè)個(gè)講給大家聽吧。首先讓我們一下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 }看到了上面的代碼,大家都知道相關(guān)增加package元素的工作在addPackage方法里面進(jìn)行的。而方法最后會(huì)返回一個(gè)PackageConfig類。PackageConfig類就是用于存放package元素信息的。為了方便讀者學(xué)習(xí),筆者希望讀者能了解一下struts-2.5.dtd這個(gè)文件。筆者現(xiàn)在不清楚有多少人了解過(guò)DTD的相關(guān)語(yǔ)法。或許很多人不知道DTD是什么東東。那么為什么要了解這個(gè)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元素節(jié)點(diǎn)到底有些什么內(nèi)容。同時(shí)了解到package元素有哪里子節(jié)點(diǎn)。通過(guò)上面的信息在和PackageConfig類的成員變量進(jìn)行對(duì)比學(xué)習(xí)的話,就比較清楚的知道為什么會(huì)有這個(gè)成員變量了。所以讓我們看一段關(guān)于PackageConfig類的代碼。如下
1 protected Map<String, ActionConfig> actionConfigs;//action的配置信息2 protected Map<String, ResultConfig> globalResultConfigs;//結(jié)果的配置信息3 protected Set<String> globalAllowedMethods;//公共允許的方法4 protected Map<String, Object> interceptorConfigs;//攔截器5 protected Map<String, ResultTypeConfig> resultTypeConfigs;//結(jié)果類型的配置信息6 protected List<ExceptionMappingConfig> globalExceptionMappingConfigs;//異常的配置信息7 protected List<PackageConfig> parents;//package元素的父配置信息8 protected String defaultInterceptorRef;//默認(rèn)的攔截器9 protected String defaultActionRef;//默認(rèn)的action 10 protected String defaultResultType;//默認(rèn)的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;讓筆者簡(jiǎn)單的講解一下關(guān)于每個(gè)變量的作用吧。如下
1.Map<String, ActionConfig> actionConfigs:用于存放action的配置信息。我們都知道一個(gè)package可以對(duì)應(yīng)多的action配置。
2.Map<String, ResultConfig> globalResultConfigs:用于存入對(duì)應(yīng)的公共結(jié)果。也許有一種情況,那就是多個(gè)action共用一個(gè)結(jié)果。
3.Set<String> globalAllowedMethods:就是action允許被調(diào)用的方法。在struts-default.xml配置文件里面設(shè)置默認(rèn)的值:execute,input,back,cancel,browse,save,delete,list,index。
4.Map<String, Object> interceptorConfigs:用于存放當(dāng)前package元素的攔截器。對(duì)于攔截器的概念的話。后面的章節(jié)會(huì)講到。
5.Map<String, ResultTypeConfig> resultTypeConfigs:用于存放action返回的結(jié)果類型。
6.List<ExceptionMappingConfig> globalExceptionMappingConfigs:用于存放action發(fā)生異常的異常配置。
7.ist<PackageConfig> parents:用于存放當(dāng)前package元素的父package元素的信息。
8.String defaultActionRef:標(biāo)示當(dāng)前package元素的默認(rèn)action。
9.String defaultResultType:標(biāo)示當(dāng)前action返回的默認(rèn)結(jié)果類型。
10.String defaultClassRef:action類默認(rèn)的父類。
11.String name:package元素的名稱
12.String namespace :package元素的命名空間
13.boolean isAbstract:package元素是否為抽象
14.boolean needsRefresh:標(biāo)示是否需要重新刷新。
15.boolean strictMethodInvocation:標(biāo)示是否啟動(dòng)SMI.關(guān)于SMI請(qǐng)找相關(guān)的知識(shí)點(diǎn)。
好了。理解了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 // 增加結(jié)果類型到newPackage里面去。 18 addResultTypes(newPackage, packageElement); 19 20 // 增加攔截器和攔截棧到newPackage里面去。 21 loadInterceptors(newPackage, packageElement); 22 23 // 設(shè)置newPackage的默認(rèn)攔截器 24 loadDefaultInterceptorRef(newPackage, packageElement); 25 26 // 設(shè)置newPackage的默認(rèn)類,即是action類的父類 27 loadDefaultClassRef(newPackage, packageElement); 28 29 // 增加公共結(jié)果到newPackage里面去。 30 loadGlobalResults(newPackage, packageElement); 31 //設(shè)置允許的方法 32 loadGlobalAllowedMethods(newPackage, packageElement); 33 34 // 增加異常處理newPackage里面去。 35 loadGlobalExceptionMappings(newPackage, packageElement); 36 37 // 加載對(duì)應(yīng)的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 // 設(shè)置newPackage默認(rèn)的ACTION 46 loadDefaultActionRef(newPackage, packageElement); 47 48 PackageConfig cfg = newPackage.build(); 49 configuration.addPackageConfig(cfg.getName(), cfg);//增加到配置類里面 50 return cfg; 51 }從上面的代碼中我們可以發(fā)現(xiàn)最后獲得package元素信息都會(huì)增加Configuration接口對(duì)應(yīng)的實(shí)例。即是DefaultConfiguration類的實(shí)例。這個(gè)方法也面也調(diào)用了很多方法來(lái)完成增加package元素。這些方法筆者并不想講解。請(qǐng)讀者自行根據(jù)筆者對(duì)方法的定義去查看源碼。而這里面有一點(diǎn)到是值得筆者注意的。那便是在PackageConfig類的實(shí)例的時(shí)候,好像用到建造者模式來(lái)實(shí)現(xiàn)。所以讀者在查看源碼的時(shí)候,如果不懂為什么作者要這樣子寫的話。請(qǐng)自行去查看相關(guān)的建造者模式的知識(shí)點(diǎn)。而加載package元素信息的工作到這里就算是結(jié)束了。
在加載package元素信息的工作結(jié)束之后,還有一件工作也是值得注意的。那便是上面reloadContainer方法代碼中出現(xiàn)的rebuildRuntimeConfiguration方法。這個(gè)方法做了什么呢?在筆者理解為創(chuàng)建一個(gè)運(yùn)行時(shí)的配置信息,用于方便調(diào)用。在什么時(shí)候調(diào)用呢?至少筆者在DefaultActionProxy類的prepare方法調(diào)用到了。這個(gè)prepare方法是在action請(qǐng)求執(zhí)行action將用到。詳細(xì)的內(nèi)容筆者會(huì)在后面的章節(jié)里面講到。rebuildRuntimeConfiguration方法最后會(huì)創(chuàng)建一個(gè)叫RuntimeConfiguration接口的實(shí)例,即是RuntimeConfigurationImpl類的實(shí)例。
| 本章總結(jié) |
本章的重點(diǎn)是知道struts2是如何加載相關(guān)的package元素節(jié)點(diǎn)信息的。那為什么要知道這部分的內(nèi)容。相信筆者心里面應(yīng)該筆者更清楚。如果不知道package元素的信息。那么struts2如何根據(jù)用戶輸入的URL來(lái)處理和運(yùn)行相關(guān)的action類呢?不知道筆者是否還記得核心機(jī)制的圖片。可以這么說(shuō)吧。到這一章相關(guān)橙黃色(Servlet Filters)部分的知識(shí)可以都結(jié)束了。我們知道如何加載相關(guān)的配置信息,知道如何加載package元素信息。而下一章筆者將對(duì)藍(lán)色(Struts core)部分的知識(shí)進(jìn)行講解。即是根據(jù)現(xiàn)有的配置信息來(lái)處理用戶發(fā)來(lái)的action請(qǐng)求。
轉(zhuǎn)載于:https://www.cnblogs.com/chenliyang/p/6552575.html
總結(jié)
以上是生活随笔為你收集整理的配置管理之PackageProvider接口的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SharedActivityContex
- 下一篇: Screened Poisson Sur