在好奇心的驱使下,查看了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);
}
}
- 在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;
}
- 最后处理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));
}