struts2的路径检索策略

来源:互联网 发布:软件编辑器手机版 编辑:程序博客网 时间:2024/06/01 03:59
对于动态页面程序,由于请求路径已经不再是一眼可以看出的页面资源,所以往往会给程序开发人员带来很多的困扰,控制层的框架不同,请求虚拟路径的地址的规则也各不相同,在众多的控制层框架中struts2请求路径有时就让程序感到无所适从,在struts2中后台服务资源名称是通过命名空间规范进行组织的,关于这一规范众说纷纭,今天我决定来一探究竟,在众多的说法中最流行的一种就是当前空间优先查找,如果没有找到,就到默认空间进行查找,如果还没找到请抛异常(action or result没找到),这应该是strut2开发过程中,最常见的一种错误.上面所说的当前空间优先,而默认空间次之是什么意思呢?
<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配置复制到所有非默认空间下去,造成了,在当前空间下没能找到就到默认空间下去找的假象,如果你仔细去体会这两种逻辑,它们是有差别的,你可以仔细去体会.

原创粉丝点击