在上一篇中我们初识了Servlet,相信大家对Servlet也都有了些了解,知道了如何创建一个Servlet,并且为其添加虚拟映射,最终发布项目,并在浏览器上请求对应的Servlet。
我们知道,只有给Servlet配置好虚拟路径,客户端才可以进行访问,但是对于Servlet的路径映射,真的只有现在所知的这么简单么?
答案当时是No了,不然怎么会有这篇文章????,下面让我们一起来探究其中的秘密吧!
1.配置多个映射路径
在上一文中,我们说到@WebServlet中的urlPatterns属性,其可以是一组匹配规则,也就是说一个Servlet是可以配置多个虚拟路径的,也就是Servlet和虚拟路径可以是一对多的一个关系(并不是多对多,一个虚拟路径只能映射一个Servlet),其具体实现如下,并修改doPost处的代码:
@WebServlet(
description = "My First Servlet",
urlPatterns = { "/HelloServlet", "/StillMe" },
initParams = {
@WebInitParam(name = "name", value = "lizishu")
})
public class HelloServlet extends HttpServlet {
//具体逻辑参看上篇文章
//...
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置返回客户端的contentType
//text/plain :纯文本格式 设置为text/html println的换行会失效
response.setContentType("text/plain;charset=utf-8");
//response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
out.println("Served at: " + request.getContextPath());
String name = this.getInitParameter("name");
out.println("name: " + name);
out.println("访问的Servle名为:" + HelloServlet.class);
}
}
可以看到,增加一个虚拟路径映射非常方便,只需在urlPatterns中新增一项即可(注意’/'不可省略),启动项目,在浏览器上输入url,可以看到,无论是输入http://localhost:8080/FirstProject/HelloServlet、还是http://localhost:8080/FirstProject/StillMe页面上得到的输出内容均一致。
urlPatterns在Servlet 3.0版本之前,都是配置在web.xml中的,每个Servlet会有一个对应的<servlet-mapping>
标签,其中可以配置多个<url-pattern>
。
2.urlPatterns匹配规则
说到Servlet虚拟路劲的匹配规则,还需要说到urlPatterns的几种匹配规则,主要有以下四种:
- 精确匹配:也就是我们在上面配置的匹配规则,需要完全相等才能匹配成功,这也是我们经常发生错误的地方,请求Servlet时的大小写拼写错误导致404;
- 路径匹配:比如想匹配以rest开头的所有请求,可以写成"/rest/*",其格式为以’/‘字符开头,并以’/*'结尾;
- 扩展名匹配:比如想匹配所有以.do结尾的请求,可以写成"*.do",其格式为以’*.’,后面跟上扩展名;
- 缺省匹配:映射路径为"/",那么这个Servlet就是当前应用的缺省Servlet,默认处理无法匹配到虚拟路径的请求。
需要注意的是,路径匹配和扩展匹配无法混合使用,即urlPattern无法写成"/rest/*.do";这也是让部分同学感到困惑的地方,Servlet的虚拟路径匹配并不是完全的按照正则来匹配的,虽然路径匹配和扩展匹配是按照正则中的通配符(*)来匹配的,这也是部分同学可以会写出特定的正则,但是却不是一个合法的虚拟路径;Servlet容器收到请求后,会将请求从上下文路径(通过request.getContextPath()获取的)处截断,使用剩余的部分来进行路径匹配,比如请求url为http://localhost:8080/FirstProject/HelloServlet,那么Servlet容器就会使用"/HelloServlet"来匹配Servlet。
最后需要注意的是,我们说了上面四种匹配规则,尤其是缺省匹配,可以匹配到任意请求,那么一个请求如果可以匹配多个Servlet的虚拟路径,那么该执行哪个Servlet?其实啊,这些匹配规则是有优先级的,具体的优先级为:精确匹配>路径匹配>扩展名匹配>缺省匹配,Servlet容器会从优先级高的虚拟路径开始匹配,匹配到后就会立刻将请求交给对应的Servlet来处理,不会再关心其他Servlet的虚拟路径是否会匹配成功。
下面我们来一组Servlet及其对应的虚拟路径:
urlPatterns | Servlet Name |
---|---|
/abc/* | Servlet1 |
/ | Servlet2 |
/abc | Servlet3 |
*.do | Servlet4 |
当请求去除上下文路径后路径为:"/abc/a.html"时,根据上述规则,会调用Servlet1;
请求为:"/abc",根据匹配优先级,会调用Servlet3;
请求为:"/abc/a.do",会匹配到’/abc/*’、’*.do’,但根据匹配优先级,会调用Servlet1;
请求为:"/a.do",会匹配到’/’、’*.do’,但根据匹配优先级,会调用Servlet4;
3.Tomcat提供的缺省Servlet
为了测试缺省Servlet,我们来进行一个测试。我们新建个SelfDefaultServlet,其urlPatterns我们配置为"/",其中的方法我们不做任何修改。
@WebServlet(
description = "Self create default Servlet",
urlPatterns = { "/" }
)
public class SelfDefaultServlet extends HttpServlet {
//...
}
我们启动项目后,在浏览器上输入http://localhost:8080/FirstProject/hahaha或者其他任意无法匹配到HelloServlet虚拟路径的请求,发现页面上的结果都如下所示,是不是这样也不错,不会报404错误了。
但是,此时我们想访问WebContent目录下的静态页面(新建的一个welcome.html文件),浏览器上输入http://localhost:8080/FirstProject/welcome.html,猜猜会发生什么?我们来一起看下结果,如图所示,请求结果并没有按照我们的想法,根据请求路径找到welcome.htm页面,而是调用了SelfDefaultServlet,是不是很懵?
其实,客户端的每个请求,都是由Servlet容器根据虚拟路径的匹配规则来进行处理的,包括静态资源。并且,如果路径输入错误(去除了自己配置的缺省Servlet后),我们常见的下面的错误,也是Servlet返回给我们,哈哈,还是很意外?
我们能通过servlet方便简单的开发网站,是因为我们站在了巨人的肩膀上,下面我们一起来看下Sun公司都为我们开发者提前做了些什么工作。Tomcat会为项目配置一个缺省的Servlet(如果项目中自行配置,则不会生效),配置文件在tomcat安装目录下conf目录中的web.xml文件中,具体内容如下,缺省的Servlet名为DefaultServlet。
<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>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
客户端请求静态资源文件时,也是由缺省的Servlet处理的(自己单独配置Servlet除外),如果请求文件能找到,就会将页面通过HttpServletResponse对象以流的方式返回给客户端,否则报404错误。
不过讲到这里,大家可以自己试一试配置了缺省Servelt时,访问welcome.html的情况(会调用SelfDefaultServlet),但是,如果我们在浏览器中输入http://localhost:8080/FirstProject/index.jsp(index.jsp是创建的第一个jsp页面)呢?会是什么样一个结果?也是调用缺省的Servlet么?真是的运行结果如下:
这是什么原因?为什么不是调用缺省的servlet了?这是因为tomcat除了缺省Serlvet外,还给我们提供一个处理jsp文件的Servlet,配置如下,因为后缀匹配的优先级高于缺省的Servlet,所以访问JSP的时候需要交由JspServlet来处理(JSP因为可能包含Java代码,所以第一次执行的时候需要先编译,这个工作由JspServlet完成)
<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>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
4.总结
本文具体讨论了urlPatterns属性的匹配规则,主要为四种,其优先级也各不相同,我们在使用时,也需要根据自己的需求自己设定urlPatterns,不过知道了匹配规则,使用起来也会方便很多,也能帮我们快速的定位错误。
又到了分隔线以下,本文到此就结束了,本文内容全部都是由博主自己进行整理并结合自身的理解进行总结,如果有什么错误,还请批评指正。
Java web这一专栏会是一个系列博客,喜欢的话可以持续关注,如果本文对你有所帮助,还请还请点赞、评论加关注。
有任何疑问,可以评论区留言。