Struts框架命名空间问题答疑

Struts框架的namespace
1. Struts设计namespace用来做什么的
Struts官网上有一段话描述了namespace的含义:
The namespace attribute subdivides action configurations into logical modules, each with its own identifying prefix. Namespaces avoid conflicts between action names. Each namespace can have its own “menu” or “help” action, each with its own implementation. While the prefix appears in the browser URI, the tags are “namespace aware”, so the namespace prefix does not need to be embedded in forms and links.
命名空间将action划分到不同的逻辑模块中,每个action都有它自己的独立前缀,这样就避免了不同逻辑模块下的action命名冲突问题。就比如项目中两个模块中都有日志功能,我们可以使用相同的菜单名称(action的name),但要保证模块名称不一样(namespace)。
所以我们在写配置文件时需要注意命名空间的名称最好是跟功能模块相关,不要乱写或者不写。

  1. 使用过程中存在的问题
    贴出struts.xml配置文件:


    /user_add.jsp


    我们知道正常请求路径应写为:http://localhost:8081/项目名/user/user_XXX.action
    这样请求当然没有问题。
    因为默认Struts会根据namespace去寻找对应的配置的action,如果找不到会到默认的命名空间去寻找。
    后来发现如果用这样的url:http://localhost:8081/项目名/user/aa/bb /user_XXX.action
    居然也可以访问成功,这样很显然就导致了访问路径的混乱。我们来分析一下。
    经过一系列测试发现根据url查找action符合以下规则:

1.假设请求路径的URI,例如url是:http://localhost:8081/项目名/path1/path2 /addUser.action
2.首先寻找namespace为/path1/path2的package,如果存在这个package,则在这个package中寻找名字为addUser的action,若找到则执行,否则转步骤5;如果不存在这个package则转步骤3。
3.寻找namespace为/path1的package,如果存在这个package,则在这个package中寻找名字为addUser的action,若找到则执行,否则转步骤5;如果不存在这个package则转步骤4。
4. 寻找namespace为/的package,如果存在这个package,则在这个package中寻找名字为addUser的action,若找到则执行,转步骤5;如果不存在转步骤5。
5. 如果存在缺省的命名空间,就在该package下查找名字为addUser的action,若找到则执行,否则页面提示找不到action;否则提示面提示找不到action。
上述一个很重要的地方就是一旦找到对应的namespace的package就停止向上级路径查找了,另外缺省命名空间(package中没有指明namespace属性或namespace属性值为空)的package的可以不存在。
以上出现的问题是在Struts-2.3.16版本测试的,在Struts2.2.X或者早期版本中没有该问题。

  1. 原因分析
    这里跟踪源码发现Struts控制器用org.apache.struts2.dispatcher.mapper.DefaultActionMapper类来寻找url对应的action。其中的getMapping方法是获取对应action的:
    这里写图片描述
    上述方法的parseNameAndNamespace就是为了得到符合uri请求的name和namespace的。
    接下来我们看这个方法里面做了怎样的处理?
    protected void parseNameAndNamespace(String uri, ActionMapping mapping, ConfigurationManager configManager)
    {
    int lastSlash = uri.lastIndexOf(“/”);//获取uri路径的最后一个斜杠位置
    String name;
    String namespace;
    String name;
    //如果写的路径直接就是action的名称,没有命名空间,则将namespace定为缺省的也就是””
    if (lastSlash == -1) {
    String namespace = “”;
    name = uri;
    }

    else
    {
      String name;
      //最后一个斜杠位置是0表示命名空间就是"/"
      if (lastSlash == 0)
      {
        String namespace = "/";
        name = uri.substring(lastSlash + 1);
      }
      //namespace不是空或者"/",可以是多路径
      else
      {
        String name;
        //下面这句就是导致该问题的原因了,如果总是用全路径寻找action则得到的namespace就是"path1/path2/"
        if (this.alwaysSelectFullNamespace)
        {
          String namespace = uri.substring(0, lastSlash);
          name = uri.substring(lastSlash + 1);
        }
        else {
          /*
           * 如果该属性alwaysSelectFullNamespace设置为false,则会递归路径去查找package,遍历所有配置文件的package,
           * 直到找到跟uri匹配的namespace。
           */
    
          Configuration config = configManager.getConfiguration();
          String prefix = uri.substring(0, lastSlash);
          namespace = "";
          boolean rootAvailable = false;
    
          for (PackageConfig cfg : config.getPackageConfigs().values()) {
            String ns = cfg.getNamespace();
            if ((ns != null) && (prefix.startsWith(ns)) && (((prefix.length() == ns.length()) || (prefix.charAt(ns.length()) == '/'))) && 
              (ns.length() > namespace.length())) {
              namespace = ns;
            }
    
            if ("/".equals(ns)) {
              rootAvailable = true;
            }
          }
    
          name = uri.substring(namespace.length() + 1);
    
          if ((rootAvailable) && ("".equals(namespace)))
            namespace = "/";
        }
      }
    }
    if (!(this.allowSlashesInActionNames)) {
      int pos = name.lastIndexOf(47);
      if ((pos > -1) && (pos < name.length() - 1)) {
        name = name.substring(pos + 1);
      }
    }
    
    mapping.setNamespace(namespace);
    mapping.setName(cleanupActionName(name));
    

    }
    上面重要的地方做了注释,可以发现这个问题是由于Struts中的alwaysSelectFullNamespace属性导致的,如果为false,则会遍历所有配置文件中的package,直到找到合理的action,寻找原则如上文所总结的那样。如果设置为true,则会严格按照uri提供的namespace寻找action,找不到就报404错误。

  2. 解决问题
    Struts的配置文件default.properties文件中有关于该属性的配置,
    这里写图片描述
    在我们struts.xml重写该属性即可。

  3. 总结
    Struts框架的namespace的确有很多的问题会出现,通过设置这个属性可以做到准备匹配对应的action,但是当form表单或者超链接写的是相对路径时,点击之后新的uri又会拼接上当前浏览器地址栏现有的路径,导致action找不到的错误。要求我们注意以下几点:
    1).页面最好写绝对路径来请求action,这样在页面跳转就不会带上namespace。
    2).配置
    防止没有严格按照namespace+action的方式搜索action处理方法。
    3).可以使用Struts注解的方式来定义Action对应的匹配url,只要将url写成以斜杠开头的绝对路径形式,就可以做到跟namespace无关,形如:
    这里写图片描述
    还要注意Action类必须定义在名字为action的包下面,不然即使打了@Action注解也不会被Struts框架扫描到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值