博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
配置管理之PackageProvider接口
阅读量:6155 次
发布时间:2019-06-21

本文共 11450 字,大约阅读时间需要 38 分钟。

 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                     configurationManager13                             .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"来解析加载。看样子不用笔者多讲大家都明白了。加载相关配置文件的代码其实就在这里开始发生的。然后就是进行供应者注册的工作。(相关的内容在《》也有讲到) 这里笔者想提一下上面提到的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
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
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
reloads = new ArrayList
(); 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文件里面的一段代码吧。

从上面的DTD信息我们很快了解到package元素节点到底有些什么内容。同时了解到package元素有哪里子节点。通过上面的信息在和PackageConfig类的成员变量进行对比学习的话,就比较清楚的知道为什么会有这个成员变量了。所以让我们看一段关于PackageConfig类的代码。如下

1  protected Map
actionConfigs;//action的配置信息 2 protected Map
globalResultConfigs;//结果的配置信息 3 protected Set
globalAllowedMethods;//公共允许的方法 4 protected Map
interceptorConfigs;//拦截器 5 protected Map
resultTypeConfigs;//结果类型的配置信息 6 protected List
globalExceptionMappingConfigs;//异常的配置信息 7 protected List
parents;//package元素的父配置信息 8 protected String defaultInterceptorRef;//默认的拦截器 9 protected String defaultActionRef;//默认的action10 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默认的ACTION46         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请求。

转载地址:http://daifa.baihongyu.com/

你可能感兴趣的文章
[物理学与PDEs]第3章习题1 只有一个非零分量的磁场
查看>>
深入浅出NodeJS——数据通信,NET模块运行机制
查看>>
onInterceptTouchEvent和onTouchEvent调用时序
查看>>
android防止内存溢出浅析
查看>>
4.3.3版本之引擎bug
查看>>
SQL Server表分区详解
查看>>
使用FMDB最新v2.3版本教程
查看>>
STM32启动过程--启动文件--分析
查看>>
垂死挣扎还是涅槃重生 -- Delphi XE5 公布会归来感想
查看>>
淘宝的几个架构图
查看>>
linux后台运行程序
查看>>
Python异步IO --- 轻松管理10k+并发连接
查看>>
Oracle中drop user和drop user cascade的区别
查看>>
登记申请汇总
查看>>
Android Jni调用浅述
查看>>
CodeCombat森林关卡Python代码
查看>>
(二)Spring Boot 起步入门(翻译自Spring Boot官方教程文档)1.5.9.RELEASE
查看>>
Shell基础之-正则表达式
查看>>
JavaScript异步之Generator、async、await
查看>>
讲讲吸顶效果与react-sticky
查看>>