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"来解析加载。看样子不用笔者多讲大家都明白了。加载相关配置文件的代码其实就在这里开始发生的。然后就是进行供应者注册的工作。(相关的内容在《Struts2 源码分析——配置管理之ContainerProvider接口》也有讲到) 这里笔者想提一下上面提到的struts-plugin.xml配置文件。这个置配文件是在插件包里面。如struts2-convention-plugin-2.5.2.jar等。也就是说XmlConfigurationProvider类也有加载插件包的配置信息功能。这一点在XmlConfigurationProvider类的loadConfigurationFiles方法里面体现的很明显。而loadConfigurationFiles方法就是用于初始化时候,加载对应的配置文件。看一下代码吧。

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的相关语法。或许很多人不知道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接口相关推荐

  1. alm系统的使用流程_支持MBSE的企业信息管理系统发展与启示

    导读:本文介绍了模型管理与MBSE.产品生命周期管理(PLM)的概念及其之间的关系,分析了不同行业的模型管理现状,提出了模型管理的解决方案与技术方向,最后给出了建设企业信息管理系统的建议,以期为企业信 ...

  2. 基于lis3dh的简易倾角仪c源码_开源网关apisix源码阅读和最佳实践

    大家应该都接手过这种项目,前人找一个开源软件改一改,发上线. 我这里便曾经遇到过类似的问题. 随着需求的增加,各种维护人员东改改西改改,原来的开源项目被改的面目全非,再也无法和上游合并. 甚至TLS协 ...

  3. SNMP MIB库的介绍

    管理信息库MIB 管理信息指在互联网的网管框架中被管对象的集合.被管对象必须维持可供管理程序读写的若干控制和状态信息.这些被管对象构成了一个虚拟的信息存储器,即管理信息库MIB. 管理程序就使用MIB ...

  4. 猿创征文|GaussDB(for openGauss):基于 GaussDB 迁移、智能管理构建应用解决方案

    文章目录 前言 一.数据库生态与技术发展 1.1.云数据库市场的高速增长 1.2.华为云 Stack+GaussDB 1.3.华为云打造 GaussDB 全场景云服务 二.何为 GaussDB(for ...

  5. 认识CAPWAP隧道及其应用

    目录 2. CAPWAP隧道 2.1 技术背景 2.2 CAPWAP介绍 2.3 CAPWAP模式 2.4 CAPWAP报文格式 2.5 AP上线流程 2.6 AP上线之二层上线 2.7图形化配置AC ...

  6. V2X前装量产的现状与挑战 | 车联网百家谈

    文章版权所有,未经授权请勿转载或使用 编者按:为推进车联网产业发展,特邀请业内专家学者共同建言献策,推出"车联网百家谈"系列.V2X前装量产是V2X渗透率提升和V2X产业发展的关键 ...

  7. openGauss数据库源码解析系列文章--openGauss简介(一)

    openGauss数据库是华为深度融合在数据库领域多年经验,结合企业级场景要求推出的新一代企业级开源数据库.此前,Gauss松鼠会已经发布了openGauss数据库核心技术系列文章,介绍了openGa ...

  8. 模型驱动的开发解决方案支撑工具的总结

    Rhapsody 是基于UML/SysML 的模型驱动开发集成环境,专注于嵌入式和实时系统.通过Rhapsody 的模型驱动体系,可以快速地将应用模型部署到实时嵌入式操作系统.Rhapsody 适应迭 ...

  9. 华为交换机如何配置ssh登录远程管理交换机

    华为交换机如何配置ssh登录远程管理交换机 如图,配置华为交换机ssh远程登录,先配置交换机的管理地址和vlan,此处为vlan10 ,把接口GE0/0/1划入vlan10,管理地址为192.168. ...

最新文章

  1. jboss_log4j.xml配置
  2. 0x03.基本算法 — 前缀和与差分
  3. javascript 函数默认参数 只适用于 ES6
  4. 计算机视觉领域最全汇总(第1部分)
  5. python 节气计算_python 生成 1900-2100 的二十四节气文件
  6. leetcode-44. Wildcard Matching
  7. idea启动tomcat时报错:Error during artifact deployment. See server log for details.
  8. 强化学习q学习求最值_通过Q学习更深入地学习强化学习
  9. google android ui,UI Automator
  10. 护壁桩嵌入深度_钻孔灌注桩嵌岩深度最少多少
  11. jfinal 普通java工程_JFinal getModel方法如何在java项目中使用
  12. Datagridview中的数据很多,加载完数据后滚动条自动到最下边,如何解决?
  13. 深入浅出PHP amp; MySQL,深入浅出 PHP MySQL
  14. java 去除敏感词
  15. FlashFXP 在win10下不能拖放操作的解决方法
  16. Inheritance: 'A' is an inaccessible base of 'B'
  17. Omni研究系列【USDT raw transaction】
  18. gitkraken点击Glo出现白屏的情况,回退回去的解决办法
  19. HBuilder打包App教程
  20. w7系统怎么开启打印机服务器,W7系统如何开启打印机服务

热门文章

  1. 计算机视觉与深度学习算法工程师面试题整理
  2. maven使用小技巧 optional
  3. 014_Vue过滤器
  4. 028_SpringBoot整合Redis
  5. 023_jdbc-mysql的CRUD操作
  6. 多维数组的索引与切片_「GCTT 出品」Go 系列教程——11. 数组和切片
  7. Java中关于路径和使用exe4j打包成ext可执行程序的一些小总结
  8. eclipse连接mysql8报错_Eclipse连接MySQL8.0.13 win10 64位
  9. java.lang.IllegalArgumentException: pointerIndex out of range
  10. java url特殊字符处理_简单实例处理url特殊符号处理(2种方法)