<package name="默认空间" extends="struts-default">
<!-- 这是一个默认空间中的aciton,主要提供验证码图形服务 -->
<action name="captchaImage" class="captchaImageAction">
<result type="captchaImage" />
</action>
</package>
<package name="/public/login" namespace="/public/login" extends="risen-default"> <!-- 这是一个默认空间中的aciton,主要提供验证码图形服务 --> <action name="login" class="captchaImageAction"> <result type="captchaImage" /> </action> </package>
可以从上面两个配置进行说起,上面第一个包是默认空间配置,第二个包的命名空间是/public/login,如果你从前台访问地址/xx/public/login/captchaImage.action如果在当前空间/public/login没有找到captchaImage配置,则系统会到默认空间中去查找,
事实并不是如此的简单,要弄清这个问题,你必须要去仔细阅读一个类源代码,这个类就是org.apache.struts2.dispatcher.mapper.DefaultActionMapper,这个类的主要功能就是将前请台的就求转换成与后台匹配一个action配置,这实际就是解决上面这个问题的关键,其中有一个关键的方法如下:
protected void parseNameAndNamespace(String uri, ActionMapping mapping, ConfigurationManager configManager) { String namespace, name; int lastSlash = uri.lastIndexOf("/"); if (lastSlash == -1) { namespace = ""; name = uri; } else if (lastSlash == 0) { // ww-1046, assume it is the root namespace, it will fallback to // default // namespace anyway if not found in root namespace. namespace = "/"; name = uri.substring(lastSlash + 1); } else if (alwaysSelectFullNamespace) { // Simply select the namespace as everything before the last slash namespace = uri.substring(0, lastSlash); name = uri.substring(lastSlash + 1); } else { // Try to find the namespace in those defined, defaulting to "" Configuration config = configManager.getConfiguration(); String prefix = uri.substring(0, lastSlash); namespace = ""; boolean rootAvailable = false; // Find the longest matching namespace, defaulting to the default for (Object cfg : config.getPackageConfigs().values()) { String ns = ((PackageConfig) cfg).getNamespace(); if (ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || prefix.charAt(ns.length()) == '/')) { if (ns.length() > namespace.length()) { namespace = ns; } } if ("/".equals(ns)) { rootAvailable = true; } } name = uri.substring(namespace.length() + 1); // Still none found, use root namespace if found if (rootAvailable && "".equals(namespace)) { namespace = "/"; } } if (!allowSlashesInActionNames && name != null) { int pos = name.lastIndexOf('/'); if (pos > -1 && pos < name.length() - 1) { name = name.substring(pos + 1); } } mapping.setNamespace(namespace); mapping.setName(name); }
如果你仔细阅读上面的方法就会发现实际先前所谈论的解析策略,上面方法的倒数第二种情况,alwaysSelectFullNamespace=true是符合的,它总是获取命名空间的最长名称,余下的部分作为action的名称,但事实是alwaysSelectFullNamespace在默认情况下的取值是false,所以正常情况系统所采用的策略是最后一种情况,是采用逐级解析策略,即尽可能长的获取命名空间名称,而不是总是取全名称,对于这种策略上,针对上面的action配置,如果请求的路径是/xx/public/login/a/b/c/captchaImage.action同样可以找到空间/public/login下的captchaImage配置,所以在出现了action or result找不到的异常是你需要查看一个alwaysSelectFullNamespace是何种配置,以及allowSlashesInActionNames 是何种配置,实在没有办法,可以将断点调试,将断点设置在此方法中,进行查看
另外,还有一点你必须得明白,系统到默认空间中去查找只是一种说法,而不是实际情况,实际情况下,系统会将默认空间下的所有action配置复制到所有非默认空间下去,造成了,在当前空间下没能找到就到默认空间下去找的假象,如果你仔细去体会这两种逻辑,它们是有差别的,你可以仔细去体会.