一、url-pattern的匹配规则
1、精确路径匹配
例如:
Servlet01 的 url-pattern 配置的是 /*
Servlet02 的 url-pattern 配置的是 /hello
这个时候如果浏览器访问 http://localhost:8080/hello 时,虽然这两个路径都是可以匹配的
但是容器会先优先进行精确路径匹配,发现 /hello 正好被 Servlet02 精确匹配,那么就去调用 Servlet02 ,就不会去理会其他的 Servlet 了.
2、目录匹配(最长路径优先匹配原则,其实也可以认为是谁的路径相似度更高就匹配谁)
例如:
Servlet01 的 url-pattern 配置的是 /aaa/*
Servlet02 的 url-pattern 配置的是 /aaa/bbb/*
这个时候如果浏览器访问 http://localhost:8080/aaa/bbb/hello 时,虽然这两个路径都是可以匹配的
但是容器会进行最长路径匹配,即 Servlet02 的路径更长,描述的更具体,所以会优先匹配上 Servlet02 ,就不会再去匹配 Servlet01 了
3、扩展名匹配
例如:
某个 Servlet 的 url-pattern 配置的是 *.do ,如果我访问 http://localhost:8080/xxx.do 时,这个时候就会根据扩展名进行匹配
二、url-pattern 的匹配顺序
1、首先进行精确路径匹配,匹配上了调用相应的 Servlet
2、如果精确路径匹配不上,接着进行目录匹配,如果匹配上了多个,选择路径最长的 Servlet
3、如果目录匹配不上,接着进行扩展名匹配,如果还匹配不上则调用 tomcat 容器中默认的 Servlet
下面我们就结合 SpringMVC 中 DispatcherServlet 的配置案例来说
三、两个web.xml
web 应用需要放在 Tomcat 容器中才能启动,Tomcat 容器内有一个默认的 web.xml 文件(放置在 tomcat 安装目录 /conf/ 下),在自己项目中配置的 web.xml 配置文件都是继承自 Tomcat 中的全局 web.xml 文件并重写其中相应配置,这种继承且重写的关系和子类继承父类并重写相关方法一样,如果子类重写了父类的方法,那么就使用子类的方法,反之就使用父类的方法.像 XML 这种格式化的文件最终会被转换成一个类去保存配置信息,所以理解 Tomcat 中全局 web.xml 文件和项目中 web.xml 文件的关系也可以类比子类重写父类方法的模式.
1、全局 web.xml( tomcat 中的 web.xml)
<!--处理静态资源的Servlet-->
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--处理jsp的Servlet-->
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
2、项目中 web.xml
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
根据就近原则,一个请求发送过来,找 web 应用服务器(tomcat)要资源时,首先会尝试和项目中的 web.xml 中进行匹配,如果匹配不上就会去全局的 web.xml 中进行匹配,如果某一个 Servlet 的 <url-pattern> 配置为 /,那么我们就称这个 Servlet 为缺省 Servlet ,凡是在 web.xml 文件中找不到有能和请求匹配的 <url-pattern> ,那么它们的访问请求都交给缺省的Servlet来处理(缺省的 Servlet 作用就是处理别的 Servlet 都不处理的请求).
四、SpringMVC 中 web.xml 前端处理器( DispatcherServelt )的常见配置
不同配置的优先级 /* > *.xxx > / (缺省的 Servlet 排在最后的原因我的理解是:只有所有的 Servlet 都匹配不上时,才会去匹配 / ,所以它的优先级是最低的)
1、前端处理器配置 /*
项目中 web.xml 的 DispatcherServlet 配置了 /* , 由于 /* 的优先级最高,那么所有的请求都会被 DispatcherServlet 处理,等于废弃了 tomcat 中的 web.xml 中的 DefaultServlet 和 JspServlet 的作用
// /* 的优先级最高,无论什么资源都会被 /* 拦截
1、访问 http//localhost:8080/index.html 时,任何资源都被前端处理器拦截了,所以 html、css、js 等静态资源也被拦截进了前端处理器,但是在
处理器层(Controller)找不到相应的处理方法处理 index.html 请求,所以会在 Controller 中报错
2、访问 http//localhost:8080/handle01 时,前端处理器拦截了请求,并把请求传递给了控制器层的 handle01 ,但是返回页面 success.jsp 的请求
也被前端处理器拦截了,不能匹配到 JspServlet,匹配不到 JspServlet 就无法对 jsp 文件进行解析,输出,渲染.
2、前端处理器配置 /
项目中 web.xml 的 DispatcherServlet 配置了 / ,代表的是缺省的 Servlet ,其它 Servlet 不处理的请求都会交给缺省的 Servlet 来处理
访问 http://localhost:8080/index.html 时,由于我们项目中 DispatcherServlet 配置的是 / ,而 tomcat 中对静态资源进行处理的
DefaultServlet 也是配置的 / ,那么根据就近原则,处理静态资源时会使用我们项目中的 DispatcherServlet ,静态资源被拦截进了
Controller 层,在 Controller 层找不到对应的处理方法,会报错
访问 http://localhost:8080/index.jsp 时,由于我们项目中 DispatcherServlet 配置的是 / ,而 tomcat 中对 jsp 进行处理的
JspServlet 配置的是 *.jsp 而 *.jsp 的优先级高于 / ,所以 jsp 的处理会交给 tomcat 中的 JspServlet,这个 Servlet 能对
jsp 资源进行解析,输出,渲染
访问 http://localhost:8080/handle01 时,由于它的路径没有任何 Servlet 能匹配上,那么就交给缺省的 Servlet ,这里有两个缺省
的 Servlet ,分别是 项目中 DispatcherServlet 和 tomcat 中 DefaultServlet ,根据就近原则,会使用项目中 web.xml 中的
DispatcherServlet ,请求被拦截进了前端处理器,前端处理器将请求传递给了控制层的 handle01 处理器,处理完后返回的
success.jsp 被 tomcat的 JspServlet 处理,渲染到页面
3、前端处理器配置 *.do 或 *.action
对于 index.html ,页面正常访问,因为 index.html 没有拦截进前端处理器中,并且 tomcat 中全局 web.xml 中的 DefaultServlet 没有被覆盖,
可以正常的渲染.
对于 index.jsp ,页面正常访问,因为 index.jsp 没有拦截进前端处理器中,并且 tomcat 中的全局 web.xml 中的 JspServlet 没有被覆盖,
可以正常的渲染.