Struts源码学习-XmlConfigurationProvider的loadPackages()方法递归调用

      最近在学习Struts2,在阅读源码的过程中,学习到了一些比较金典的算法逻辑,现在分享一个递归算法的使用:个人感觉这个算法非常的精妙,见识了递归算法使用的高明之处,现在将源码粘贴出来:

1、类:XmlConfigurationProvider的loadPackages方法,该方法主要是将配置文件(XML)文件中的package节点的配置信息读取出来:

 1     public void loadPackages() throws ConfigurationException {
 2         List<Element> reloads = new ArrayList<Element>();
 3         for (Document doc : documents) {
 4             Element rootElement = doc.getDocumentElement();
 5             NodeList children = rootElement.getChildNodes();
 6             int childSize = children.getLength();
 7 
 8             for (int i = 0; i < childSize; i++) {
 9                 Node childNode = children.item(i);
10 
11                 if (childNode instanceof Element) {
12                     Element child = (Element) childNode;
13 
14                     final String nodeName = child.getNodeName();
15 
16                     if ("package".equals(nodeName)) {
17                         PackageConfig cfg = addPackage(child);//该方法调用如下18                         if (cfg.isNeedsRefresh()) {
19                             reloads.add(child);
20                         }
21                     }
22                 }
23             }
24             loadExtraConfiguration(doc);
25         }
26 
27         if (reloads.size() > 0) {
28             reloadRequiredPackages(reloads);
29         }
30 
31         for (Document doc : documents) {
32             loadExtraConfiguration(doc);
33         }
34 
35         documents.clear();
36         configuration = null;
37     }
addPackage方法如下:
调用了buildPackageContext方法,进行package的内容构造,包括,名字,命名空间,是否抽象,以及extends的名称,如果,该package需要刷新,则证明,当前的包的需要继承的包的名字,还没有被初始化到configure中,当所有的初始化,操作完成后,在进行这些需要重新加载的文件进行刷新,这时候,他的继承的包就可以找到了,就可以进行继承关系的实现了。
 1  /**
 2      * Create a PackageConfig from an XML element representing it.
 3      */
 4     protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
 5         PackageConfig.Builder newPackage = buildPackageContext(packageElement);//该方法参考如下
 6 
 7         if (newPackage.isNeedsRefresh()) {
 8             return newPackage.build();
 9         }
10         
--否则加载该包,是一个新的包,下面是加载这个包中的所有种类的配置文件 11 if (LOG.isDebugEnabled()) { 12 LOG.debug("Loaded " + newPackage); 13 } 14 15 // add result types (and default result) to this package 16 addResultTypes(newPackage, packageElement); 17 18 // load the interceptors and interceptor stacks for this package 19 loadInterceptors(newPackage, packageElement); 20 21 // load the default interceptor reference for this package 22 loadDefaultInterceptorRef(newPackage, packageElement); 23 24 // load the default class ref for this package 25 loadDefaultClassRef(newPackage, packageElement); 26 27 // load the global result list for this package 28 loadGlobalResults(newPackage, packageElement); 29 30 // load the global exception handler list for this package 31 loadGobalExceptionMappings(newPackage, packageElement); 32 33 // get actions 34 NodeList actionList = packageElement.getElementsByTagName("action"); 35 36 for (int i = 0; i < actionList.getLength(); i++) { 37 Element actionElement = (Element) actionList.item(i); 38 addAction(actionElement, newPackage); 39 } 40 41 // load the default action reference for this package 42 loadDefaultActionRef(newPackage, packageElement); 43 44 PackageConfig cfg = newPackage.build();
--最后,将该配置文件放入到list中,假如,某个的包的extend的名字为当前的包,就可以直接找出来
45 configuration.addPackageConfig(cfg.getName(), cfg); 46 return cfg; 47 }

 

 1  /**
 2      * This method builds a package context by looking for the parents of this new package.
 3      * <p/>
 4      * If no parents are found, it will return a root package.
 5      */
 6     protected PackageConfig.Builder buildPackageContext(Element packageElement) {
 7         String parent = packageElement.getAttribute("extends");
 8         String abstractVal = packageElement.getAttribute("abstract");
 9         boolean isAbstract = Boolean.valueOf(abstractVal).booleanValue();
10         String name = StringUtils.defaultString(packageElement.getAttribute("name"));
11         String namespace = StringUtils.defaultString(packageElement.getAttribute("namespace"));
12 
13 
14         if (StringUtils.isNotEmpty(packageElement.getAttribute("externalReferenceResolver"))) {
15             throw new ConfigurationException("The 'externalReferenceResolver' attribute has been removed.  Please use " +
16                     "a custom ObjectFactory or Interceptor.", packageElement);
17         }
18 
19         PackageConfig.Builder cfg = new PackageConfig.Builder(name)
20                 .namespace(namespace)
21                 .isAbstract(isAbstract)
22                 .location(DomHelper.getLocationObject(packageElement));
23 
24         --判断是否有extend的节点,在这里,我们可以方法Struts2使用了其他类库分装的公共组件
例如:StringUtils.isNotEmpty,同样还有StringUtils.isEmpty,对于StringUtils类,一个是apache提供的 ,另一个是Spring提供的,这两个类的功能都非常的强大,几乎涵盖了所有的针对Srring和数组的操作, 25 if (StringUtils.isNotEmpty(StringUtils.defaultString(parent))) { // has parents, let's look it up 26 --buildParentsFromString方法的说明如下: 27 List<PackageConfig> parents = ConfigurationUtil.buildParentsFromString(configuration, parent); 28 29 if (parents.size() <= 0) { 30 cfg.needsRefresh(true); 31 } else { 32 cfg.addParents(parents); 33 } 34 } 35 36 return cfg; 37 }

 

 1 public static List<PackageConfig> buildParentsFromString(Configuration configuration, String parent) {
 2         if ((parent == null) || ("".equals(parent))) {
 3             return Collections.emptyList();
 4         }
 5 
 6         StringTokenizer tokenizer = new StringTokenizer(parent, ", ");
 7         List<PackageConfig> parents = new ArrayList<PackageConfig>();
 8 
 9         while (tokenizer.hasMoreTokens()) {
10             String parentName = tokenizer.nextToken().trim();
11 
12             if (!"".equals(parentName)) {
该方法与上面的configuration.addPackageConfig(cfg.getName(), cfg)方法可以说是遥相呼应
13 PackageConfig parentPackageContext = configuration.getPackageConfig(parentName); 14 15 if (parentPackageContext != null) { 16 parents.add(parentPackageContext); 17 } 18 } 19 } 20 21 return parents; 22 }

在这里,开始介绍那个递归函数使用:他是loadPackages()方法中的

如果当前的需要重新加载的文件大于0,则进行重新加载。

1  if (reloads.size() > 0) {
2       reloadRequiredPackages(reloads);
3  }

--递归的使用:

 1      if (reloads.size() > 0) {
 2             List<Element> result = new ArrayList<Element>();
 3             for (Element pkg : reloads) {
--再次调用,
4 PackageConfig cfg = addPackage(pkg); 5 if (cfg.isNeedsRefresh()) {
--判断父亲是否需要重新加载
6 result.add(pkg); 7 } 8 }
         如果当前的result的集合大于0,并且需要重新加载的包和再次加载的包的个数不一直,证明还没有循环完成,
9 if ((result.size() > 0) && (result.size() != reloads.size())) { 10 reloadRequiredPackages(result); 11 return; 12 } 13 --在这里,递归正式调用结束 14 // Print out error messages for all misconfigured inheritence packages 15 if (result.size() > 0) { 16 for (Element rp : result) { 17 String parent = rp.getAttribute("extends"); 18 if (parent != null) { 19 List<PackageConfig> parents = ConfigurationUtil.buildParentsFromString(configuration, parent); 20 if (parents != null && parents.size() <= 0) { 21 LOG.error("Unable to find parent packages " + parent); 22 } 23 } 24 } 25 } 26 } 27 }

--总结:判断该递归方法的继续使用,使用了两个判定条件,如果,不成立,那么其他的就是无法找到父包的包了,这个算法非常的精妙。

 

 

 

posted on 2013-02-01 22:48  Coldest Winter  阅读(531)  评论(0编辑  收藏  举报