最近在学习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 }
--总结:判断该递归方法的继续使用,使用了两个判定条件,如果,不成立,那么其他的就是无法找到父包的包了,这个算法非常的精妙。