struts2中如何根据请求路径定位到详细的访问action

Posted HelloBingle

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了struts2中如何根据请求路径定位到详细的访问action相关的知识,希望对你有一定的参考价值。

在struts2中在访问一个菜单链接时,我们只需要将相应的package 命名空间和 action的name进行组合,并加上相应的后缀,就可以直接访问到相应的Action了,那么这个过程是如何进行的,多个相同命名空间的 package是如何满足互不冲突的呢,这就需要详细了解struts2中是如何解析路径信息,并根据访问路径寻找相应的action配置了。

整个过程我们可以分成以下几个步骤进行处理

  1. 解析xml,将所有可以访问到的路径信息进行保存
  2. 根据访问请求信息,取其中可用的路径
  3. 根据路径进行查找,最终查找到我们所需要的Action

解析XML

首先我们知道一个package以及action的配置如下所示:

1

2

3

4

5

<package name="packageName" extends="struts-default" namespace="/">

    <action name="logic" class="detailAction" method="init">

        <result name="success">/jsp/success.jsp</result>

    </action>

   </package>

即主要包括包名,命名空间以及action的名称。那么我们就可以将这一系列的信息通过一个类似PackageConfig的对象进行组织起来。这就是struts2里面所使用的packageConfig。我们来看它的简单定义:

1

2

3

4

    protected Map<String, ActionConfig> actionConfigs;

......

    protected String name;

    protected String namespace = "";

其中就包括了我们所知道的包名,命名空间,以及使用action名称和每个action配置的一个map。那么,整个工程中有许多的package,struts如何进行处理呢,那就需要用到这里面的命名空间了。

我们可以把命名空间作为key,然后把每个package里面的所有action配置都作为value进行保存起来,那么使用key表示命名空间,action配置作为value,其中将action的配置再一次map化,使用以下数据结构来进行定义存储。

1

Map<String, Map<String, ActionConfig>> namespaceActionConfigs = new LinkedHashMap<String, Map<String, ActionConfig>>();

双map化,第一个key表示命名空间,第二个key表示action的名称即路径,最终的value即每个action的相应信息,这样针对以下的一个路径:

1

/contextPath/模块名/功能名/action名/操作.action

我们只需要将上下文和后面的.action除去,剩下的 /模块名/功能名/action名/操作,实际上就是一个命名空间和action路径的一个组合。我们只需要按照/分隔符挨个到map中查找即可。如果存 在 以模块名为命名空间的package信息,我们就可以直接定义到第二个map,然后 直接使用key 功能名/action名/操作进行直接匹配到相应的action即可以了。

那么整个数据结构的构建过程即在类DefaultConfiguration的方法buildRuntimeConfiguration中,如下所示:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

        Map<String, Map<String, ActionConfig>> namespaceActionConfigs = new LinkedHashMap<String, Map<String, ActionConfig>>();

        Map<String, String> namespaceConfigs = new LinkedHashMap<String, String>();

 

//这里即按每个package进行循环,依次放入map中

        for (PackageConfig packageConfig : packageContexts.values()) {

 

                 String namespace = packageConfig.getNamespace();

                Map<String, ActionConfig> configs = namespaceActionConfigs.get(namespace);

 

                Map<String, ActionConfig> actionConfigs = packageConfig.getAllActionConfigs();

 

                for (Object o : actionConfigs.keySet()) {

                    String actionName = (String) o;

//这里先取得原先放在大map中的集合信息

                    ActionConfig baseConfig = actionConfigs.get(actionName);

//这里即是将当前package中的action配置再根据命名空间合并到大的map中

                    configs.put(actionName, buildFullActionConfig(packageConfig, baseConfig));

                }

 

                namespaceActionConfigs.put(namespace, configs);

            }

        }

 

        return new RuntimeConfigurationImpl(namespaceActionConfigs, namespaceConfigs);

解析Action信息

接下来就是查找Action的过程了,当一个请求到来时,struts2首先要得知哪个action能够处理这个请求,如果没有可以处理的请求,则进行报 错或者其它提示。首先,我们知道,这个查找过程必须先去掉上下文之前的所有信息,其次再去掉后缀,只留下中间与命名空间和action名称组合的字符串, 然后再进行查找。这个过程的描述即在类DefaultActionMapper的方法getMapping中进行的,如下所示:

01

02

03

04

05

06

07

08

09

10

        ActionMapping mapping = new ActionMapping();

//这一步去头和上下文

        String uri = getUri(request);

//这一步去分号和后面的所有信息

        int indexOfSemicolon = uri.indexOf(";");

        uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;

//这一步去后续及后面的所有信息,如.action

        uri = dropExtension(uri, mapping);

//这一步为真正的查找过程

        parseNameAndNamespace(uri, mapping, configManager);

查找的具体过程与整个路径的组织有关,整个实现可以由以下步骤确定。

  1. 按/进行倒序查找,如果没有,则认为命名空间为"",整个路径即是action名称
  2. 如果在第1位,即以/开头,则认为命名空间为/,后面的即是action名称
  3. 否则配置了全命名空间搜索,即贪心搜索,即认为所有/之前的都是命名空间,后面即是action名称
  4. 否则将所有的在第一步组织的map中进行最长度化匹配,即匹配到的最长的那个命名空间即是我们所要的,后面的则是action名称
  5. 最后,如果配置了action路径中不存在/,则将整个action路径中/之间的去掉,最后的不包括/的即是action名称

以上的步骤中,1,2,3,4都是查找过程,最后的第5步为处理过程。整个的实现逻辑即与上述的一致,如下所示:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

   String namespace, name;

        int lastSlash = uri.lastIndexOf("/");

//这里即是第1步,即在没有上下文时,整个路径 就没有/

        if (lastSlash == -1) {

            namespace = "";

            name = uri;

//第2步,即存在命名空间为/时,或者为""时,这时/即在第1位

        } 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);

//第3步,如果贪心搜索,则最长的路径即为命名空间

        } else if (alwaysSelectFullNamespace) {

            // Simply select the namespace as everything before the last slash

            namespace = uri.substring(0, lastSlash);

            name = uri.substring(lastSlash + 1);

//第4步,最长化匹配搜索

        } else {

            // Try to find the namespace in those defined, defaulting to ""

            Configuration config = configManager.getConfiguration();

            String prefix = uri.substring(0, lastSlash);

            namespace = "";

            // 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;

                    }

                }

            }

            name = uri.substring(namespace.length() + 1);

        }

 

//这是第5步,针对action名称处理

        if (!allowSlashesInActionNames && name != null) {

            int pos = name.lastIndexOf(‘/‘);

            if (pos > -1 && pos < name.length() - 1) {

                name = name.substring(pos + 1);

            }

        }

这样即完成了查找过程,但这里仅是一个查找,并不是一个最终匹配,因为还存在着根据这个查找到的匹配结果如何映射到最终action对象的过程,即映射到actionConfig上。

映射actionConfig

整个映射过程可以分成以下几个步骤进行

  1. 首先,根据默认的匹配进行查找,如果能够查找到,就认为是最终的actionConfig,在其中也会对action名称进行模式匹配
  2. 否则对命名空间进行*号匹配,尝试进行模式匹配
  3. 最后则尝试使用默认的空命名空间进行查找

1    其实就是首先根据命名空间查找相应的子map,即actionName和actionConfig的集合,然后再在子map中查找,如果子map中查不到,则使用模式匹配。再找不到就进入到步骤2了。
2    在步骤2时,首先也会使用模式匹配查找到对应的命名空间,如果有可匹配的命名空间,就使用该命名空间再次重复第一步操作
3    还没找到,就使用默认的命名空间即""进行查找

整个实现在类DefaultActionMapper的getActionConfig方法中,如下所示:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

//使用第一步操作进行匹配

            ActionConfig config = findActionConfigInNamespace(namespace, name);

 

//使用第二步操作进行命名空间的模式匹配

            // try wildcarded namespaces

            if (config == null) {

                NamespaceMatch match = namespaceMatcher.match(namespace);

                if (match != null) {

                    config = findActionConfigInNamespace(match.getPattern(), name);

 

                    // If config found, place all the matches found in the namespace processing in the action‘s parameters

                    if (config != null) {

                        config = new ActionConfig.Builder(config)

                                .addParams(match.getVariables())

                                .build();

                    }

                }

            }

 

//使用第3步的默认匹配

            // fail over to empty namespace

            if ((config == null) && (namespace != null) && (!"".equals(namespace.trim()))) {

                config = findActionConfigInNamespace("", name);

            }

 

            return config;

至此,整个定位过程结束。如果此处仍找不到actionConfig对象,那么就直接抛出相应的异常了,即常见的

There is no Action mapped for namespace {0} and action name {1}.
There is no Action mapped for action name {0}.

 

转载请标明出处:i flym
本文地址:http://www.iflym.com/index.php/code/201302270001.html

以上是关于struts2中如何根据请求路径定位到详细的访问action的主要内容,如果未能解决你的问题,请参考以下文章

Struts2框架action路径问题心得----》页面url请求怎么找action

Servlet中关于路径的小结

struts2 的学习

Xcode如何快速的根据错误日志定位到相应的

java 怎么根据httpPost 和httpClient 等,传图片到服务器!

iis7 无法验证对路径的访问