Struts2中URL处理

  • Post author:
  • Post category:其他


在好奇心的驱使下,查看了Struts对于URL的处理源码,在Struts中,主要是需要通过URL找到对应的ActionMapping设置,也就是在Struts.xml中配置的action,其寻找过程如下:

1. 在web.xml中,配置了.do结尾的请求,都会被struts的

org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter

过滤器过滤。

2. 在StrutsPrepareAndExecuteFilter中的doFilter中,执行寻找ActionMapping,并执行对应的action方法逻辑

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                chain.doFilter(request, response);
            } else {
                prepare.setEncodingAndLocale(request, response);
                // 创建Struts值栈ValueStack
                prepare.createActionContext(request, response);
                // 设置当前分发工具类Dispatcher到当前线程中
                prepare.assignDispatcherToThread();
                request = prepare.wrapRequest(request);
                // 寻找相应的ActionMapping
                ActionMapping mapping = prepare.findActionMapping(request, response, true);
                if (mapping == null) {
                    boolean handled = execute.executeStaticResourceRequest(request, response);
                    if (!handled) {
                        chain.doFilter(request, response);
                    }
                } else {
                    execute.executeAction(request, response, mapping);
                }
            }
        } finally {
            prepare.cleanupRequest(request);
        }
    }
  1. 在PrepareOperations.findActionMapping中的逻辑
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
        ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
        if (mapping == null || forceLookup) {
            try {
                mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
                if (mapping != null) {
                    request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
                }
            } catch (Exception ex) {
                dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
            }
        }

        return mapping;
    }

  1. 最后处理URL的地方在DefaultActionMapper.parseNameAndNamespace中
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
            //找到对应的namespace(operator/quotetask)
            for (PackageConfig cfg : config.getPackageConfigs().values()) {
                String ns = 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;
                }
             }
              // 找到对应的namspace,并用uri中,去除namespace,用于获取对应的action名字
            name = uri.substring(namespace.length() + 1);

            // Still none found, use root namespace if found
            if (rootAvailable && "".equals(namespace)) {
                namespace = "/";
            }
        }
        // 以上两个URL之所以能访问到同一个action, 关键在此处, 通过前面去掉namespace后,最后获得两个actionName为: operator/quotetask/queryByTaskId 与 /queryByTaskId .在以下代码中,直接获取最后一个"/"后的串,则都获得queryByTaskId.并找到对应的ActionMapping
        if (!allowSlashesInActionNames) {
            int pos = name.lastIndexOf('/');
            if (pos > -1 && pos < name.length() - 1) {
                name = name.substring(pos + 1);
            }
        }

        mapping.setNamespace(namespace);
        mapping.setName(cleanupActionName(name));
    }