URL-PATTERN语义详解
在web.xml文件中,以下语法用于定义映射。
① 以"/"开头和以"/*"结尾的是用来做路径映射的。
② 以前缀"*."开头的是用来做扩展映射的。
③ "/" 是用来定义default servlet映射的。
④ 剩下的都是用来定义详细映射的。比如: /aa/bb/cc.action
⑤ /direcotry/*.jsp不支持,容器无法判别是路径映射还是扩展映射。【注意】:"/" 的设置将会将当前指定的 servlet 设为web应用的默认servlet,原来web容器的默认servlet将被替换。
!!!
/ 和 /*
之间的区别:
<url-pattern>/</url-pattern>:将servlet定义为容器默认servlet,当没有其他servlet能够处理当前请求时,由该servlet进行处理。 <url-pattern>/*</url-pattern>:会匹配所有url - 路径型的和后缀型的url(包括/login , *.jsp , *.js 和 *.html 等)
【1】Filter的url-pattern
filter只要匹配成功,都将会加载 ApplicationFilterFactory部分源码如下:
/** * Return <code>true</code> if the context-relative request path * matches the requirements of the specified filter mapping; * otherwise, return <code>false</code>. * * @param filterMap Filter mapping being checked * @param requestPath Context-relative request path of this request */ private boolean matchFiltersURL(FilterMap filterMap, String requestPath) { // Check the specific "*" special URL pattern, which also matches // named dispatches if (filterMap.getMatchAllUrlPatterns()) return (true); if (requestPath == null) return (false); // Match on context relative request path String[] testPaths = filterMap.getURLPatterns(); for (int i = 0; i < testPaths.length; i++) { if (matchFiltersURL(testPaths[i], requestPath)) { return (true); } } // No match return (false); } /** * Return <code>true</code> if the context-relative request path * matches the requirements of the specified filter mapping; * otherwise, return <code>false</code>. * * @param testPath URL mapping being checked * @param requestPath Context-relative request path of this request */ private boolean matchFiltersURL(String testPath, String requestPath) { if (testPath == null) return (false); // Case 1 - Exact Match if (testPath.equals(requestPath)) return (true); // Case 2 - Path Match ("/.../*") if (testPath.equals("/*")) return (true); if (testPath.endsWith("/*")) { if (testPath.regionMatches(0, requestPath, 0, testPath.length() - 2)) { if (requestPath.length() == (testPath.length() - 2)) { return (true); } else if ('/' == requestPath.charAt(testPath.length() - 2)) { return (true); } } return (false); } // Case 3 - Extension Match if (testPath.startsWith("*.")) { int slash = requestPath.lastIndexOf('/'); int period = requestPath.lastIndexOf('.'); if ((slash >= 0) && (period > slash) && (period != requestPath.length() - 1) && ((requestPath.length() - period) == (testPath.length() - 1))) { return (testPath.regionMatches(2, requestPath, period + 1, testPath.length() - 2)); } } // Case 4 - "Default" Match return (false); // NOTE - Not relevant for selecting filters }
三种正确的匹配方式:
emp可替换为替他变量或者字符串。*.do
可换其他,如*.jsp
。
<url-pattern>/*</url-pattern> <url-pattern>*.do</url-pattern> <url-pattern>/emp/*</url-pattern> 第二种为扩展名映射,其他两种为路径映射。特定功能的filter可使用具体路径匹配。
两种错误的filter匹配方式:
<url-pattern>/</url-pattern> // 一定不可设置成该方式! <url-pattern>/emp/</url-pattern>
使用错误的匹配方式,提交form表单,method为post:
【2】Serlvet的url-pattern
servlet只会加载一个匹配成功的,它的匹配原则就是:找到唯一一个最适合的Servlet!
① 首先精确匹配,如定义了两个Servlet:
Servlet1为/foo.htm,Servlet2是/* ; 请求URL为http://localhost/foo.htm; 那么只有Servlet1匹配成功;
② 如果精确匹配不成功,那么会使用第二个原则"最长路径匹配":
如Servlet1为/foo/*,Servlet2为/* ; 请求的URL为:http://localhost/foo/foo.htm; 那么Servlet1匹配成功;
③ 最后根据后缀进行匹配;
④ 如果有default servlet, 那么将会使用默认的servlet进行处理。
即优先级为:
精确匹配>路径匹配>后缀匹配(扩展匹配)>default servlet
如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。如果应用定义了一个default servlet,则容器会将请求丢给default servlet。
【几种正确的servlet匹配方式:】
① /
--servlet将会替代容器内建的servlet成为default-servlet;
② /*
--路径映射;
③ /emp/*
--更具体的路径映射;
④ *.do
--扩展名映射。
这里需要说明servlet配置/ 与 /*
时的注意事项。
原贴位置如下:
http://stackoverflow.com/questions/4140448/difference-between-and-in-servlet-mapping-url-pattern
当某个servlet的url-pattern设置为"/*"时:
The /* on a servlet overrides all other servlets, including all servlets provided by the servletcontainer such as the default servlet and the JSP servlet. Whatever request you fire, it will end up in that servlet. This is thus a bad URL pattern for servlets. Usually, you'd like to use /* on a Filter only. It is able to let the request continue to any of the servlets listening on a more specific URL pattern by calling FilterChain#doFilter().
/*
配置的servlet将会比其他所有的(包括容器内建的)servlet具有更高的优先级,那么所有的请求都会在这个servlet结束 !!!
这是非常糟糕的!通常,你更喜欢只在filter中使用 /*
。它将通过调用doFilter()方法使请求继续。
当某个servlet的url-pattern设置为"/"时:
The / doesn't override any other servlet. It only replaces the servletcontainer's builtin default servlet for all requests which doesn't match any other registered servlet. This is normally only invoked on static resources (CSS/JS/image/etc) and directory listings. The servletcontainer's builtin default servlet is also capable of dealing with HTTP cache requests, media (audio/video) streaming and file download resumes. Usually, you don't want to override the default servlet as you would otherwise have to take care of all its tasks, which is not exactly trivial (JSF utility library OmniFaces has an open source example). /*下面的说法我并不赞同*/ This is thus also a bad URL pattern for servlets. As to why JSP pages doesn't hit this servlet, it's because the servletcontainer's builtin JSP servlet will be invoked, which is already by default mapped on the more specific URL pattern *.jsp.
解释如下:
这种形式不会覆盖任何其它的servlet,它仅仅替换了servlet容器中内建的默认servlet。
这种形式通常只用来请求静态资源(CSS/JS/image等)和展示目录的列表。
servlet容器内建的默认servlet同样可以处理HTTP cache请求、媒体(声音/视频)流以及文件的下载。
通常来说,你不会想要覆盖这个默认的servlet,否则,你将不得不自己处理一些琐碎的任务。
因此,对于sevlet来说,这同样是一个糟糕的URL模式。
说到为什么JSP页面的请求并不会命中这个servlet,那是因为servlet容器内建的JSP servlet(不是default-servlet)将会被调用,而这个容器内建的JSP servlet已经默认地映射在了*.jsp上。<url-pattern></url-pattern>
- 如果为空呢?
Then there's also the empty string URL pattern . This will be invoked when the context root is requested. This is different from the <welcome-file> approach that it isn't invoked when any subfolder is requested. This is most likely the URL pattern you're actually looking for in case you want a "home page servlet". I only have to admit that I'd intuitively expect the empty string URL pattern and the slash URL pattern / be defined exactly the other way round, so I can understand that a lot of starters got confused on this. But it is what it is.
这种空串的形式。当上下文的根被请求的时候,它将被调用。
这与<welcome-file>的方式是不同的,因为这种形式在当任何子目录被请求的时候不会被调用。
当你期望一个“首页servlet”的时候,这种URL模式就是适合你的。
【可能由于环境原因,测试空的url-pattern失败 !】