为什么web.xml配置servlet映射要多一个servlet-name

为什么web.xml配置servlet映射要多一个servlet-name

昨天在看《Head First Servlets & JSP》二刷JavaWeb的时候,突然注意到一个点,为什么web.xml配置servlet映射要多一个servlet-name?

像这样:

<?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">
    
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.microsoft.servlet.MyServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myservlet</url-pattern>
    </servlet-mapping>

</web-app>

我直接从url-pattern映射到servlet-class不就行了吗?为什么要多一层servlet-name呢?我查了CSDN和知乎等一系列博文,大家似乎都只说了映射的查找顺序,抑或是一笔带过,询问技术群的大佬,不是装x就是讲的模糊(也可能是我太菜),我打算用此篇博文记录我的心路历程和现在的理解

理解 v1.0

我提出这个问题,得到的第一个回答是:

“你是学生,为什么要有名字呢?”

第二个回答是:

“总得找到爹吧?”

我内心这么翻译:

“他是servlet,为什么不该有个名字呢?得有个统一管理他们的爸爸。”

说的是有道理,但是我的名字难道不可以用url体现吗?不可以直接通过类名体现吗?

思来想去,我决定暂时这么理解:servlet-name就像一个map容器,装了多对servlet-class和url的映射

Tomcat容器扫描web.xml,通过反射拿到了所有servlet-name的map容器,通过用户请求的url,给出对应的class

// 通过反射拿到了map,并add了所有映射
Class servletClass = map.get("urlPattern");
// 通过模板实例化servlet类

可是这样一来,在我加载时我就要创建并填充所有的map容器,似乎效率并不高,为了统一管理这样做,似乎这个设计并不合理

理解 v2.0

我得到了另外一个兄弟的回复:

“我不止是/myservlet这一条url要map这个MyServlet,我可能还有/myservlet1,/myservlet2。早期这么设计,也算是一定程度的解耦,以达到复用的目的。”

我说了我的理解v1.0后,他也给整晕了:

“一个name可以bind多个url吧,这就可以复用了,这是规范。现在这些不都被SpringMVC磨平了吗。。。”

emm…我也不再过问

按照他的说法:

<?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">
    
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.microsoft.servlet.MyServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myservlet</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myservlet1</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myservlet2</url-pattern>
    </servlet-mapping>

</web-app>

多组映射复用,但是似乎还是有点抽象啊;我在想,我能不能这么理解,结合理解v1.0

故事1:

操场上散步遇到女神,过来问我:“你们班最帅的那个男生叫什么啊?”

我:“额…你说丁黄冈吗?”

只见她羞答答地直冲我们班,找丁黄冈去了…

故事2:

操场上散步又遇到女神,过来问我:“你们班打篮球很强的那个男生叫什么啊?”

我:“就…就是…丁黄冈啊?”

只见她又羞答答地直冲我们班,找丁黄冈去了…

(别问,问就是老舔狗了)

我们把故事转换成代码:(把servlet想成同学)

<?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">
    
    <servlet>
        <servlet-name>丁黄冈</servlet-name>
        <servlet-class>一班.丁黄冈同学</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>丁黄冈</servlet-name>
        <url-pattern>/最帅的</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>丁黄冈</servlet-name>
        <url-pattern>/打球很强的</url-pattern>
    </servlet-mapping>

</web-app>

url可能是对一种特征(或功能)的描述,所指向的对象,可能是同一个。丁黄冈可以同时又帅又有钱又高,还会打球,可是只有这么一个丁黄冈!可是,你告诉我他是几班的xxx不就行了,为什么还是要多个name呢,名字可以在class中体现啊?(疯狂牛角尖)

理解 v3.0

我将最后的希望寄托于stack overflow,结果还真找到了知音!!!

第一条评论:

为什么你会觉得少写这几行,会让xml运行效率更高?

emm…不是很懂…

第二条评论:

通常来说,servlet容器会去扫描web.xml,并在内存中创建servlet实例对象。它比每次发出请求时读取web.xml都要快得多。

有点类似缓存???不是很懂,但我觉得涉及到了servlet创建的问题:

首先Servlet实例为一个单例对象,从头到尾只有一个,而其创建在于第一次被调用(第一次处理请求),这是一种默认行为。

既然说是默认了,那么你也想到了,肯定可以手动更改了?当然,猜想正确。

其可以通过修改web.xml这个配置文件:

<servlet>
   <load-on-startup>1</load-on-startup>
</servlet>

该属性可以将servlet对象的创建修改为:服务器启动时就创建该对象。

就比如说,我已经在开启容器之初就创建好了,我可以通过名字直接拿,就不用再用全限定类名去反射拿模板创建了,因为已经有了一个实例对象,我直接拿来用就好了;没有的话,再去创建。

理解 v3.1

还有一个答案评价最高的老哥是这么说的:

它允许一个servlet可以有有多个servlet映射。

<servlet>
    <servlet-name>Servlet1</servlet-name>
    <servlet-path>foo.Servlet</servlet-path>
</servlet>
<servlet-mapping>
    <servlet-name>Servlet1</servlet-name>
    <url-pattern>/enroll</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>Servlet1</servlet-name>
    <url-pattern>/pay</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>Servlet1</servlet-name>
    <url-pattern>/bill</url-pattern>
</servlet-mapping>

请注意,web.xml在应用程序启动期间仅读取和解析一次(在扫描时候,我先拿到了servlet类模板,但我不一定创建,需要时我再拿这个模板创建实例化对象),而不是您认为的每个HTTP请求都读取和解析。

从Servlet 3.0开始,存在@WebServlet注释,该注释使此样板最小化:

@WebServlet("/enroll")
public class Servlet1 extends HttpServlet {

总结

我的疑问大抵是打消了。

servlet-class相对应的就是方便容器读取xml,拿到类模板,name相对来说,就像做个记录。用理解v1.0来说,理解为map似乎也说的过去:因为我填充的是类模板,还未实例化,效率并没有想象那么低。“为什么还是要多个name呢,名字可以在class中体现啊?”我之前这个问题,现在看来似乎有点傻…

如果我是直接略过servlet-name这一层,直接绑定class和url,容器不一定认得啊,他可能每次都要去读取xml,找类模板,反射创建对象,效率不是更低???

反过来看第一个老哥的评论:

为什么你会觉得少写这几行,会让xml运行效率更高?

是啊,Why do you think a different XML schema would affect runtime performance?

更新

三种映射模式

  • 完全匹配
<servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-path>foo.MyServlet</servlet-path>
</servlet>
<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/examples/myservlet.do</url-pattern>
</servlet-mapping>
  • 目录匹配
<servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-path>foo.MyServlet</servlet-path>
</servlet>
<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/examples/*</url-pattern>
</servlet-mapping>
  • 扩展名匹配
<servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-path>foo.MyServlet</servlet-path>
</servlet>
<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

优先级

容器匹配顺序:

  • 完全
    • 如果同时有多个匹配满足,则优先最长匹配原则
  • 目录
  • 后缀
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值