• 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 }

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

     

     

     

  • 相关阅读:
    反射
    EFCore笔记之异步查询
    EFCore笔记之查询数据
    Json扩展 (转)
    C语言学习笔记之成员数组和指针
    asp中cookie欺骗/注入原理与防范
    简单的php Mysql类(查询 删除 更新)
    PHP四舍五入精确小数位及取整
    CentOS中配置LNMP环境打开提示File not found
    WIN中SharePoint Server 2010 入门安装部署详解
  • 原文地址:https://www.cnblogs.com/caroline/p/2889656.html
Copyright © 2020-2023  润新知