传送门
环境
- tomcat:apache-tomcat-8.5.43,tomcat官网,直接下载8.5.45
- jdk:1.8
- 开发工具:idea
主题:新建的web项目为什么默认访问index.jsp
- tomcat启动时,会读取web.xml文件,项目中的web.xml以及tomcat自带的(位置:apache-tomcat-8.5.43\conf\web.xml),tomcat自带两个servlet,下面是这两个servlet的映射规则(web.xml里面写的)
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 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>
- 配置自己的web.xml,依据 普通web项目添加servlet 博客中所描述的项目(后面所说的配置都是依据这个项目),为HelloServlet和HiServlet多配置几个映射,配置如下
<!-- 配置和映射Servlet -->
<servlet>
<!--Servlet 注册的名字-->
<servlet-name>helloServlet</servlet-name>
<!--Servlet 的全类名-->
<servlet-class>xin.yangshuai.servlet.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!--需要和某一个servlet 节点的 servlet-name 字节点的文本节点一致-->
<servlet-name>helloServlet</servlet-name>
<!--映射具体的访问路径:/ 代表当前 WEB 应用的根目录-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置和映射Servlet -->
<servlet>
<servlet-name>hiServlet</servlet-name>
<servlet-class>xin.yangshuai.servlet.HiServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hiServlet</servlet-name>
<url-pattern>/hi</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hiServlet</servlet-name>
<url-pattern>/hi/*</url-pattern>
</servlet-mapping>
一、映射规则
- 项目启动时,存储所有的映射规则(方便理解,用基本的数据结构做解释,实际数据类型为HashMap):将servlet的url-pattern做为key,servlet-name做为value,保存到map(理解为map)中。所有的url-pattern不能重复(可以与tomcat自带的两个servlet的url-pattern重复,如果重复,会覆盖tomcat自带的servlet的映射规则),具体可以看WebXml.java类的getServletMappings方法。下面是简化数据结构后,用表格描述映射规则
key | value |
---|---|
/hi | hiServlet |
/hello | helloServlet |
/* | helloServlet |
/hi/* | hiServlet |
.jsp | jsp |
.jspx | jsp |
/ | helloServlet |
二、匹配规则
- 映射规则转化为匹配规则(方便理解,用基本的数据结构做解释,实际数据类型为Mapper.MappedWrapper数组或Mapper.MappedWrapper对象),将所有的映射规则存储到四个匹配规则中,具体可以看Mapper.java类internalMapWrapper方法。下面是简化数据结构后,用表格描述匹配规则
key | value | 匹配规则 | 对象名 | 对象类型 |
---|---|---|---|---|
/hello | helloServlet | 精确匹配 | exactWrappers | Mapper.MappedWrapper[] |
/hi | hiServlet | 精确匹配 | exactWrappers | Mapper.MappedWrapper[] |
(空串) | helloServlet | 前缀匹配 | wildcardWrappers | Mapper.MappedWrapper[] |
/hi | hiServlet | 前缀匹配 | wildcardWrappers | Mapper.MappedWrapper[] |
jsp | jsp | 扩展名匹配 | extensionWrappers | Mapper.MappedWrapper[] |
jspx | jsp | 扩展名匹配 | extensionWrappers | Mapper.MappedWrapper[] |
(空串) | helloServlet | 默认匹配 | defaultWrapper | Mapper.MappedWrapper |
注意:这里的对象名和对象类型都是java类中使用的,可以去看源码
- 匹配优先级如下,排在前面的优先级高
匹配规则 | 对象名 | 对象类型 | 映射特征(url-pattern) |
---|---|---|---|
精确匹配 | exactWrappers | 数组 | 具体路径,例如:/hello |
前缀匹配 | wildcardWrappers | 数组 | 以/*结尾,例如:/*,/hi/* |
扩展名匹配 | extensionWrappers | 数组 | 以*.开头,例如:*.jsp,*.html |
默认匹配 | defaultWrapper | 一个对象 | /,其它匹配都无法匹配时,使用默认匹配 |
- tomcat自带的web.xml配置了welcome-file-list节点,该节点的子节点即为默认访问页面(welcome-file),welcome-file配置的顺序会影响到实际的默认访问,我们可以在自己的web.xml文件中配置welcome-file-list节点以覆盖tomcat自带的,tomcat自带的web.xml中配置的welcome-file-list节点如下
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
- 如果是默认访问,即请求根路径(ip:port/项目名/) 时,如果我们自定义的servlet没有配置“/*”前缀匹配,那么请求路径无法依据精确匹配、前缀匹配及扩展名匹配被任何servlet匹配到,此时会增加一个资源文件(welcomeResources)匹配方式,先拼接welcome-file的值再进行如上三种匹配,具体规则如下:
1、如果只有一个welcome-file时,直接将welcome-file的值拼接到根目录后面进行匹配
2、如果存在多个welcome-file时,按照顺序依次尝试,如果依据精确匹配、前缀匹配方式可以找到对应的servlet或者物理真实存在该文件,则使用该welcome-file的值拼接到根目录后面进行匹配,如果不存在这样的welcome-file,则使用第一个welcome-file的值拼接到根目录后面进行匹配。
注意:这里说的物理真实存在表示,在项目的webapp文件夹下存在这样的文件,即文件名和后缀与welcome-file的值相同
三、实例验证
仍然使用 前文提到的项目 ,servlet和项目结构都不变。项目的web.xml发生变化,覆盖tomcat自带的web.xml的welcome-file-list,现在web.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- tomcat自带的两个servlet,位置在tomcat根目录/conf/web.xml里面,把其中的映射粘贴过来-->
<!--
The mapping for the default servlet
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
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>
-->
<!--tomcat自带web.xml中的欢迎页,直接粘贴过来-->
<!--
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
-->
<!-- 配置helloServlet -->
<servlet>
<!--Servlet 注册的名字-->
<servlet-name>helloServlet</servlet-name>
<!--Servlet 的全类名-->
<servlet-class>xin.yangshuai.servlet.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置hiServlet -->
<servlet>
<servlet-name>hiServlet</servlet-name>
<servlet-class>xin.yangshuai.servlet.HiServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!--配置welcome-file-list-->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 配置helloServlet映射 -->
<!-- 配置hiServlet映射 -->
</web-app>
- 1、servlet配置了/*映射,则用此servlet来处理默认访问
web.xml
<!-- 配置helloServlet映射 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/index.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置hiServlet映射 -->
<servlet-mapping>
<servlet-name>hiServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
运行结果:hiServlet配置了/*映射,则由hiServlet来处理默认访问
- 2、servlet没有配置/*映射,只有一个welcome-file时,则直接将welcome-file的值拼接到根目录后面进行匹配
web.xml
<!--配置welcome-file-list-->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<!-- 配置helloServlet映射 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/index.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置hiServlet映射 -->
<servlet-mapping>
<servlet-name>hiServlet</servlet-name>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
运行结果:没有servlet配置/*,只有一个welcome-file,值为index.html,则默认访问相当于:http://localhost:8080/first_page/index.html,hiServlet配置的精确匹配,所有由hiServlet来处理默认访问。
- 3、其实welcome-file不用写成一个页面的样子,可以随意的写,可以没有后缀,可以有层级。
web.xml
<!--配置welcome-file-list-->
<welcome-file-list>
<welcome-file>hh/index.html</welcome-file>
</welcome-file-list>
<!-- 配置helloServlet映射 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hh/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置hiServlet映射 -->
<servlet-mapping>
<servlet-name>hiServlet</servlet-name>
<url-pattern>/hh/index.html</url-pattern>
</servlet-mapping>
运行结果::没有servlet配置/*,只有一个welcome-file,则默认访问相当于:http://localhost:8080/first_page/hh/index.html,hiServlet配置的精确匹配,所以由hiServlet来处理默认访问。
注意:没有精确匹配时,也会寻找可以进行前缀匹配、扩展名匹配的servlet进行匹配。
- servlet没有配置/*映射,如果存在多个welcome-file时,按照顺序依次尝试,如果存在依据精确匹配、前缀匹配方式获取的servlet或者物理真实存在该文件,则使用该welcome-file的值拼接到根目录后面进行匹配,如果不存在这样的welcome-file,则使用第一个welcome-file的值拼接到根目录后面进行匹配。
注意:存在多个welcome-file,默认请求尝试拼接welcome-file的值时,是不考虑扩展名匹配的。
- 4、尝试拼接:依据物理真实存在文件
web.xml
<!--配置welcome-file-list-->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 配置helloServlet映射 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- 配置hiServlet映射 -->
<servlet-mapping>
<servlet-name>hiServlet</servlet-name>
<url-pattern>/hi</url-pattern>
</servlet-mapping>
运行结果:没有servlet配置/*,存在多个welcome-file时,按照顺序依次尝试,首先尝试拼接index.html,但是index.html并非物理真实存在,并且不能精确匹配或者前缀匹配,所以index.html被舍弃,同样道理,index.htm也会被舍弃,然后尝试index.jsp,index.jsp物理真实存在,所以拼接index.jsp,默认访问相当于:http://localhost:8080/first_page/index.jsp,此路径可以由tomcat自带的名为jsp的servlet进行扩展名(*.jsp)匹配,这也就是我们新建项目默认访问的页面。
- 5、我们可以覆盖默认的映射规则,比如覆盖*.jsp
web.xml
<!-- 配置hiServlet映射 -->
<servlet-mapping>
<servlet-name>hiServlet</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
运行结果:与上面相同的分析过程相同,默认访问相当于:http://localhost:8080/first_page/index.jsp,由于hiServlet覆盖了*.jsp映射规则,所以由hiServlet处理默认访问。
- 6、尝试拼接:依据前缀匹配
web.xml
<!--配置welcome-file-list-->
<welcome-file-list>
<welcome-file>hh/index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 配置helloServlet映射 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
<!-- 配置hiServlet映射 -->
<servlet-mapping>
<servlet-name>hiServlet</servlet-name>
<url-pattern>/hh/*</url-pattern>
</servlet-mapping>
运行结果:没有servlet配置/*,存在多个welcome-file时,按照顺序依次尝试,首先尝试拼接hh/index.html,可以看出,hiServlet配置了/hh/*的映射规则,符合前缀匹配,所以拼接hh/index.html,默认访问相当于:http://localhost:8080/first_page/hh/index.html,所以由hiServlet处理默认访问。
注意:除了依据前缀匹配,也可以依据精确匹配。
7、尝试拼接:不能依据扩展名匹配
web.xml
<!--配置welcome-file-list-->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 配置helloServlet映射 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- 配置hiServlet映射 -->
<servlet-mapping>
<servlet-name>hiServlet</servlet-name>
<url-pattern>/hh</url-pattern>
</servlet-mapping>
运行结果:没有servlet配置/*,存在多个welcome-file时,按照顺序依次尝试,此时默认访问相当于:http://localhost:8080/first_page/index.jsp,依据物理真实存在文件拼接。helloServlet虽然配置了*.html后缀匹配规则,但是并没有拼接index.html。
- 8、尝试拼接:无法依据精确匹配,前缀匹配和物理真实存在文件匹配,则使用第一个拼接。
我们将项目的index.jsp删掉(改个名相当于删除了)
web.xml(与上面相同)
<!--配置welcome-file-list-->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 配置helloServlet映射 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- 配置hiServlet映射 -->
<servlet-mapping>
<servlet-name>hiServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
运行结果:没有servlet配置/*,存在多个welcome-file时,按照顺序依次尝试,此时依据精确匹配,前缀匹配和物理真实存在文件匹配都无法匹配到,所以拼接第一个,默认访问相当于:http://localhost:8080/first_page/index.html,此时只有helloServlet配置了*.html扩展名匹配,所以由helloServlet来处理默认访问。
总结
- 匹配优先级
精确匹配
前缀匹配
扩展名匹配
资源文件匹配(如果是默认访问)
默认匹配 - 默认访问拼接原则
如果是默认访问,即请求根路径(ip:port/项目名/) 时,如果我们自定义的servlet没有配置“/*”前缀匹配,那么请求路径无法依据精确匹配、前缀匹配及扩展名匹配被任何servlet匹配到,此时会增加一个资源文件(welcomeResources)匹配方式,先拼接welcome-file的值再进行如上三种匹配,具体规则如下:
1、如果只有一个welcome-file时,直接将welcome-file的值拼接到根目录后面进行匹配
2、如果存在多个welcome-file时,按照顺序依次尝试,如果依据精确匹配、前缀匹配方式可以找到对应的servlet或者物理真实存在该文件,则使用该welcome-file的值拼接到根目录后面进行匹配,如果不存在这样的welcome-file,则使用第一个welcome-file的值拼接到根目录后面进行匹配。
如有错误,欢迎指正!!!
参考:https://www.cnblogs.com/fangjian0423/p/servletContainer-tomcat-urlPattern.html