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值的URLS
7 } 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 package
name CDATA #REQUIRED
extends CDATA #IMPLIED
namespace CDATA #IMPLIED
abstract CDATA #IMPLIED
strict-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请求。

配置管理之PackageProvider接口的更多相关文章

  1. Struts2 源码分析——配置管理之PackageProvider接口

    本章简言 上一章讲到关于ContainerProvider的知识.让我们知道struts2是如何注册相关的数据.也知道如何加载相关的配置信息.本章笔者将讲到如何加载配置文件里面的package元素节点 ...

  2. Struts2 源码分析——配置管理之ContainerProvider接口

    本章简言 上一章笔者讲到关于Dispatcher类的执行action功能,知道了关于执行action需要用到的信息.而本章将会讲到的内容也跟Dispatcher类有关系.那就是配置管理中的Contai ...

  3. Struts2.3.15.1源码浅析

    Struts2 两大运行主线: 1.初始化主线:初始化主线主要是为Struts2创建运行环境(此处的环境与Struts2身处的Web环境是有区别的),初始化入口StrutsPrepareAndExec ...

  4. struts2是如何加载相关的package元素节点信息的

    这不是一篇纯技术文章,而是一篇分享我个人在前后端分离路上收获的点点滴滴的文章,以此来为准备尝试前后端分离或者想了解前后端分离的童鞋做一个大体的讲解. 上一家公司是家小公司,做了一年的全栈开发,对前端的 ...

  5. Struts2源码浅析-ConfigurationProvider

    ConfigurationProvider接口 主要完成struts配置文件 加载 注册过程 ConfigurationProvider接口定义 public interface Configurat ...

  6. 剖析SSH核心原理(一)

      在我前面的文章中,也试图总结过SSH,见 http://blog.csdn.net/shan9liang/article/details/8803989 ,随着知识的积累,总感觉以前说得比较笼统, ...

  7. 基于特定领域国土GIS应用框架设计及应用

              基于特定领域国土GIS应用框架 设计及应用              何仕国 2012年8月16日   摘要: 本文首先讲述了什么是框架和特定领域框架,以及与国土GIS 这个特定领 ...

  8. Spring IOC 低级容器解析

    1.IOC是什么 IOC-Inversion of Control,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不 ...

  9. HBase应用开发回顾与总结系列之四:HBase配置管理类接口设计

      利用Eclipse进行HBase应用开发时,至少需要确定三个配置信息,如下表所示: #hbase config #HMaster服务部署主机及端口号 hbase.master=hdp-wuyong ...

随机推荐

  1. marquee.js

      图片无缝滚动工具类 CreateTime--2018年3月7日17:11:03 Author:Marydon /** * 图片无缝滚动 * @description * 将要滚动的图片复制一份作为 ...

  2. 使用C#开发ActiveX控件[Obsolete]

    文章出处:http://www.cnblogs.com/yilin/archive/2009/09/15/1567332.html 附件下载(源代码+安装文件+教程) 0. 前言 ActiveX控件以 ...

  3. unity3d为对象添加脚本的两种方法

    首先添加一个物体,然后新建一个C#脚本.接下去有两种方法把C#脚本与物体绑定. 1.在类声明上方添加如下代码: [AddComponentMenu("a/b")] 这句话表示在该物 ...

  4. Android新手入门2016(10)--GridView

    本文来自肥宝传说之路.引用必须注明出处! GridView跟ListView一样是多控件布局.实现九宫图是最方便的. 还是先看看图,没图说个鸡鸡是不是 如上图.是一种应用方式.在每一个格子里面.放入应 ...

  5. 点滴积累【C#】---验证码,ajax提交

    效果: 思路: 借用ashx文件创建四位验证,首先生成四位随机数字.然后创建画布,再将创建好的验证码存入session,然后前台进行button按钮将文本框中的值进行ajax请求到后台,和sessio ...

  6. uva 11400 - Lighting System Design(动态规划 最长上升子序列问题变型)

    本题难处好像是在于 能够把一些灯泡换成电压更高的灯泡以节省电源的钱 .所以也才有了对最优方案的探求 好的处理方法是依照电压从小到大排序.仅仅能让前面的换成后面的.也就满足了把一些灯泡换成电压更高的灯泡 ...

  7. 在 Linux 客户端配置基于 Kerberos 身份验证的 NFS 服务器

    在这篇文章中我们会介绍配置基于 Kerberos 身份验证的 NFS 共享的整个流程.假设你已经配置好了一个 NFS 服务器和一个客户端.如果还没有,可以参考 安装和配置 NFS 服务器[2] - 它 ...

  8. 斑马Zebra ZPLII指令集中文说明解释

      我们最常用的斑马(Zebra)条码打印机,应用ZPLII命令来控制打印,说明书中有每条指令的详细说明及相关示例,下面是各指令的中文释义: ^A 对Zebra内置点阵字体缩放 ^A(可缩放/点阵字体 ...

  9. mybatis expected at least 1 bean which qualifies as autowire candidate for this dependency

    错误原因:没有引入相应mapper接口,导致spring没有找到依赖 解决方法一:使用注解的方法: 首先在spring配置文件中添加 <bean class="org.mybatis. ...

  10. traceroute/tracert--获取网络路由路径

    traceroute 是用来检测发出数据包的主机到目标主机之间所经过的网关数量的工具.traceroute 的原理是试图以最小的TTL发出探测包来跟踪数据包到达目标主机所经过的网关,然后监听一个来自网 ...