`
Rainbow702
  • 浏览: 1076720 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类

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

阅读更多

 

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


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

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方法中调用了一个方法 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
0
0
分享到:
评论
1 楼 jf168 2016-03-08  
呵呵,不好意思,我认为博文有笔误。 原文的“上面的 ② 和 ③ 分别对应着 [struts.convention.package.locators] 与 [struts.convention.action.packages] 这两个配置项。” 应为:“上面的 ③ 和 ② ……”

相关推荐

    STRUTS2 Convention零配置

    - **包定位器**:`struts.convention.package.locators`属性用于指定如何定位Action类所在的包。例如,可以设置为`actions.fore.LoginAction`,这意味着路径`/fore/login`会映射到这个Action。 #### 4. **结果视图...

    简述STRUTS2_Convention零配置

    - `struts.convention.package.locators`:定义Action的根包,影响Action的命名空间映射。 - `struts.convention.action.packages`:除了默认的四个包(action, actions, struts, struts2),这个常量可以指定额外...

    Struts2配置精要之常量constant配置详解(包括零配置Convention 的常量)

    - `struts.convention.package.locators`: 定义包定位器,决定如何从类路径中找到Action类。 - `struts.convention.result.path`: 指定结果文件(如JSP)的默认路径。 - `struts.convention.action.suffix`: 设置...

    STRUTS2零配置.doc

    2. `struts.convention.package.locators`: 定义Action的根包,影响Action的命名空间映射。例如,如果设置为`fore`,那么`actions.fore.LoginAction`将映射到`/login`而不是`/fore/login`。 3. `struts.convention....

    struts2注解使用.doc

    - `struts.convention.package.locators`:此常量定义了Action的搜索根包,可以自定义Action的命名空间。 - `struts.convention.action.packages`:用于指定额外的包,Convetion插件会在这个包及其子包中寻找...

    Struts2注解使用说明文档

    `struts.convention.package.locators`定义了Action的根包,影响Action的命名空间;`struts.convention.action.packages`则可以指定额外的包,让Convention去搜索Action类。 4. **Action名称映射**:Action类的名字...

    struts2注解详解

    可以通过配置项 `struts.convention.package.locators` 来指定搜索的位置,例如:`actions`, `action`, `struts`, `struts2` 等。这表示框架会在这些命名空间下查找 Action 类。此外,还可以通过 `struts.convention...

Global site tag (gtag.js) - Google Analytics