struts2 中,[struts.convention.package.locators] 与 [struts.convention.action.packages] 的区别_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > struts2 中,[struts.convention.package.locators] 与 [struts.convention.action.packages] 的区别

struts2 中,[struts.convention.package.locators] 与 [struts.convention.action.packages] 的区别

 2015/5/15 16:04:49  Rainbow702  程序员俱乐部  我要评论(0)
  • 摘要:对于convention插件中的这两个配置项一直不是很清楚它们之间的区别,今天看了下struts2的源码,大概弄清楚了。下面以下图的工程目录为基础,直入主题进行说明。首先,我贴上一段从官网上看到的话:Youcanalsotelltheplugintousedifferentstringstolocaterootpackagesusingthepropertystruts.convention.package.locators.Finally
  • 标签:区别 struts

?

对于convention插件中的这两个配置项一直不是很清楚它们之间的区别,今天看了下struts2的源码,大概弄清楚了。下面以下图的工程目录为基础,直入主题进行说明。


首先,我贴上一段从官网上看到的话:

class="txt">You can also tell the plugin to use different strings to locate root packages using the property struts.convention.package.locators. Finally, you can tell the plugin to search specific root packages using the property struts.convention.action.packages.

?官网地址:http://struts.apache.org/docs/convention-plugin.html#ConventionPlugin-Codebehindhelloworld

?对于上面这段话,我起初没有明白他的意思,甚至是误解了他的意思。(也正是如此,才决定看下源码来了解下真实情况)。

通过debug发现下面这个类,是使用这两个配置的核心类:

org.apache.struts2.convention.PackageBasedActionConfigBuilder

?从名字我们可以看出来,这个类是用来通过package来配置Action的情报的。

在这个类里,以下这个方法作为入口方法,进行配置Action的:

STEP1:

public void buildActionConfigs() {
        //setup reload class loader based on dev settings
        initReloadClassLoader();
        
        if (!disableActionScanning) {
            if (actionPackages == null && packageLocators == null) {
                throw new ConfigurationException("At least a list of action packages or action package locators " +
                        "must be given using one of the properties [struts.convention.action.packages] or " +
                        "[struts.convention.package.locators]");
            }

            if (LOG.isTraceEnabled()) {
                LOG.trace("Loading action configurations");
                if (actionPackages != null)
                    LOG.trace("Actions being loaded from action packages " + Arrays.asList(actionPackages));
                if (packageLocators != null)
                    LOG.trace("Actions being loaded using package locators " + Arrays.asList(packageLocators));
                if (excludePackages != null)
                    LOG.trace("Excluding actions from packages " + Arrays.asList(excludePackages));
            }

            Set<Class> classes = findActions();
            buildConfiguration(classes);
        }
    }

?我们需要关注的是最后两个的两个方法: findActions() 和?buildConfiguration()。

我们先来看一下findActions():

STEP2:

?

protected Set<Class> findActions() {
        Set<Class> classes = new HashSet<Class>();
        try {
            if (actionPackages != null || (packageLocators != null && !disablePackageLocatorsScanning)) {
                
                // By default, ClassFinder scans EVERY class in the specified
                // url set, which can produce spurious warnings for non-action
                // classes that can't be loaded. We pass a package filter that
                // only considers classes that match the action packages
                // specified by the user
                Test<String> classPackageTest = getClassPackageTest();
                ClassFinder finder = new ClassFinder(getClassLoaderInterface(), buildUrlSet().getUrls(), true, this.fileProtocols, classPackageTest);

                Test<ClassFinder.ClassInfo> test = getActionClassTest();
                classes.addAll(finder.findClasses(test));
            }
        } catch (Exception ex) {
            if (LOG.isErrorEnabled())
                LOG.error("Unable to scan named packages", ex);
        }

        return classes;
    }

?其中getClassPackageTest()的实现为:

?

STEP3:

?

protected Test<String> getClassPackageTest() {
        return new Test<String>() {
            public boolean test(String className) {
                return includeClassNameInActionScan(className);
            }
        };
    }

?这里返回了一个匿名类,它的test方法中调用了一个方法?monospace;">includeClassNameInActionScan() ,这个方法是一个关键方法,下面会说到。

?

回到 SETP2 处,其中还调用了一个 ?getActionClassTest() ?方法,其实现如下:

?

protected Test<ClassFinder.ClassInfo> getActionClassTest() {
        return new Test<ClassFinder.ClassInfo>() {
            public boolean test(ClassFinder.ClassInfo classInfo) {
                
                // Why do we call includeClassNameInActionScan here, when it's
                // already been called to in the initial call to ClassFinder?
                // When some action class passes our package filter in that step,
                // ClassFinder automatically includes parent classes of that action,
                // such as com.opensymphony.xwork2.ActionSupport.  We repeat the
                // package filter here to filter out such results.
                boolean inPackage = includeClassNameInActionScan(classInfo.getName());
                boolean nameMatches = classInfo.getName().endsWith(actionSuffix);

                try {
                    return inPackage && (nameMatches || (checkImplementsAction && com.opensymphony.xwork2.Action.class.isAssignableFrom(classInfo.get())));
                } catch (ClassNotFoundException ex) {
                    if (LOG.isErrorEnabled())
                        LOG.error("Unable to load class [#0]", ex, classInfo.getName());
                    return false;
                }
            }
        };
    }

可以看到,这里再次调用了?includeClassNameInActionScan() 方法(所以,说这个方法是关键方法)。

?

另外,这里的"boolean nameMatches = classInfo.getName().endsWith(actionSuffix);"的作用,是用来判断一个类是否是以“actionSuffix”结尾的,“actionSuffix”的值,可以通过以下的配置项来进行指定:

?

<constant name="struts.convention.action.suffix" value="Action"/>

如上面的配置,如果一个类是以Action结尾的话,会有可能被当成一个Action。但到底能不能作为一个Action,请看return语句:

?

return inPackage && (nameMatches || (checkImplementsAction && com.opensymphony.xwork2.Action.class.isAssignableFrom(classInfo.get())));

?从该语句来看,只有符合以下条件的类,才能被当成Action类:

① 必须在指定的包下面

② 名字须以actionSuffix”结尾;或者,须实现了“com.opensymphony.xwork2.Action”类

对于第②点,我想大家应该是比较容易理解的。那么,第①点呢?要如何才能判断一个类是否在指定的包下面呢?这个回到我们上面说的关键方法:includeClassNameInActionScan() ,其实现如下:

?

protected boolean includeClassNameInActionScan(String className) {

        String classPackageName = StringUtils.substringBeforeLast(className, ".");

        if(excludePackages != null && excludePackages.length > 0) {
            WildcardHelper wildcardHelper = new WildcardHelper();

            //we really don't care about the results, just the boolean
            Map<String, String> matchMap = new HashMap<String, String>();

            for(String packageExclude : excludePackages) {
                int[] packagePattern = wildcardHelper.compilePattern(packageExclude);
                if(wildcardHelper.match(matchMap, classPackageName, packagePattern)) {
                    return false;
                }
            }
        }

        if (actionPackages != null) {
            for (String packageName : actionPackages) {
                String strictPackageName = packageName + ".";
                if (classPackageName.equals(packageName)
                        || classPackageName.startsWith(strictPackageName))
                    return true;
            }
        }

        if (packageLocators != null && !disablePackageLocatorsScanning) {
            for (String packageLocator : packageLocators) {
                if (classPackageName.length() > 0
                        && (packageLocatorsBasePackage == null || classPackageName
                        .startsWith(packageLocatorsBasePackage))) {
                    String[] splitted = classPackageName.split("\\.");

                    if (StringTools.contains(splitted, packageLocator, false))
                        return true;
                }
            }
        }

        return false;
    }

这个方法可以分成三步来看:

?

① 如果参数className对应的类属于某个“excludePackage”,那么它理所当然的需要被排除

?

② 如果①不满足,那么将判断?参数className对应的类的包名是否是“actionPackages”,或者以 “actionPackages”+"." 开头,如果是的话就认为它是被包含在指定包内的。否则将被排除。

举例说明:

如果?"actionPackages" 的值为 “rainbow”, 那么对于上面的File3Action或者其他Actin来说,都是不满足②这条的,因为它们的包名是以“com”开头的,而非“rainbow”。

?

③ 如果②不满足,那么将判断?参数className对应的类的包名以“.”分割之后,得到的每个字符串,是否至少少有一个被包含在“packageLocators”对应的数组中,如果是的话就认为它是被包含在指定包内的。否则将被排除。

举例说明:

如果“packageLocators”的值被设定为“action,example”,那么File3Action就是不满足③这条的,因为File3Action的包名为“com.rainbow”,以“.”分割之后,得到两个字符串“com”和“rainbow”,这两个字符串都不在“action,example”之列,所以File3Action将会被排除掉。

?

到此,就告一段落了。 上面的 ② 和 ③ 分别对应着?[struts.convention.package.locators] 与 [struts.convention.action.packages] 这两个配置项。

从源码来看,[struts.convention.action.packages]?的优先级是比?[struts.convention.package.locators]?要高的,而且,对于[struts.convention.action.packages]我可以配置成像下面这种的:

?

<constant name="struts.convention.action.packages" value="com.rainbow.strtus2"/>

这样一样,所有“com.rainbow.strtus2”包及其子包下的Action类会被注册成Action类。

?

?

----------------分割线-----------------------

其实,在PackageBasedActionConfigBuilder类中,还有很有一个很关键的方法:?buildConfiguration()。

这个方法用来配置Action的相关信息,比如 namespace之类的。

不过,本文未准备说明这个方法。有兴趣的童鞋可以自行查看。

?

  • 大小: 16.4 KB
  • 查看图片附件
发表评论
用户名: 匿名