SpringMVC中各类请求和匹配Servlet处理的问题详解

前言:本次文章主要是介绍在SpringMVC项目中,我们的页面上的所有请求是如何被服务器进行处理的。

前置知识(重点):

  • 页面上的所有请求,到服务器上都需要有一个对应的Servlet来进行处理。
  • Servlet中的service()方法就是用来处理所匹配到的请求的。
  • 一个请求最少被一个Servlet处理,如果处理过程没有发生请求转发或重定向,那么浏览器发送的请求只会被服务器的一个Servlet的service()处理,并携带处理结果返回给浏览器。
  • 页面中,除了普通的超链接、表单提交等常见的能发起请求的标签外,像<script>、<img>等这些标签,通过src或href属性引入静态资源的方式,也属于向服务器发起请求,因此也必须认识到它是一个请求。此外,src属性的值是URL,作用是链接目标资源,所以也比较好理解。
  • Tomcat服务器有一个全局的web.xml配置文件,该配置文件对所有部署在该Tomcat实例上的web工程都有效。
  • 每个web工程都有自己的一份web.xml配置文件,同时web工程又会受到Tomcat服务器的全局web.xml配置文件的作用,在一个web工程中,自身的web.xml配置文件的优先级高于Tomcat的web.xml的优先级。 (局部优先原则)

DispatcherServlet (前端控制器)

       首先,对于SpringMVC而言,核心组件就是前端控制器DispatcherServlet,它就是一个Servlet,可以用来处理请求。
       所以一般而言,我们配置前端控制器的时候,都会将它的<url-pattern>值配置为"/"。

<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
 <servlet>
 	 <servlet-name>springMVC</servlet-name> 
 	 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet- class> 
</servlet> 
<servlet-mapping> 
	<servlet-name>springMVC</servlet-name> 
	<!--设置springMVC的核心控制器所能处理的请求的请求路径 /所匹配的请求可以是/login或.html或.js或.css方式的请求路径 但是/不能匹配.jsp请求路径的请求 --> 
	<url-pattern>/</url-pattern> 
</servlet-mapping>

那么问题来了,为什么我们配置的是"/" ?
       原因:因为"/" 代表了除".jsp"请求外的所有请求,配置"/",前端控制器可以替我们拦截所有的请求,除了".jsp"请求。

       如此一来,浏览器上的所有请求,除了".jsp"请求,其他的请求都“老有所依,幼有所养”了,都有一个Servlet来处理它们了。

那么接下来,又出现两个问题了:

  1. ".jsp"请求怎么办?谁来处理它们呢?
  2. 当我们请求页面的静态资源,如.jpg图片、.png图片、.js文件、.css文件等时,前端控制器会如何处理这些请求呢?

JspServlet

       在Tomcat的全局web.xml配置文件中,配置了如下的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>

       该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>

       诶,我们可以看到,这个JspServlet,用来拦截"*.jsp" 或 "*.jspx"的请求,也就是说,当客户端发起请求访问服务器的".jsp"页面时,是不需要由前端控制器来进行处理的,而是用JspServlet来对这类请求进行处理。
       此外,我们知道,其实每一个jsp页面,在经过服务器的编译解析后,会被翻译成一个Servlet,所以我们常说,Jsp的本质就是Servlet,所以我们也不难理解,当客户端请求jsp页面资源时,其实已经有一个对应的Servlet来处理这个请求了,就是jsp页面翻译出来的Servlet。

       那么到此,我们的第一个问题,".jsp"请求的归属问题,就解决啦。

       下来开始第二个问题的解决,对于请求静态资源,前端控制器是如何处理的?
       先说结论,由于前端控制器我们一般配置的拦截路径是"/",所以它会拦截我们的.png、.jpg、.js等静态资源请求,而这类请求我们是不会创建对应的控制器方法来处理的,因此如果我们不做一些其他的配置处理,那么页面中的这些静态资源都会报404错误,也就是找不到相对应的资源。
       那么该如何解决这个问题呢?噔噔噔噔,就轮到我们的另一个Servlet大佬出场啦!

DefaultServlet

       还是在我们的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>

       该Servlet的拦截路径配置如下:

<!-- The mapping for the default servlet -->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

       可以看出,这个DefalueServlet的拦截路径和前端控制器相同,都是"/",也就是拦截除".jsp"以外的所有请求。

那么这个DefaultServlet的作用是什么呢?
       各位在第一次接触JavaWeb时,或者会发现,当我们不写任何的自定义Servlet,而只部署一些静态资源如html页面在服务器上时,其实我们是可以通过浏览器访问到这些静态资源的,对吧?
       那为什么我们没注册任何的Servlet,却可以通过浏览器访问如html这类静态资源呢?
       其实,这就是我们的DefalutServlet的功劳啦!当我们的请求没有任何的自定义Servlet来处理时,就会由我们的DefalutServlet来进行处理,它配置在全局的web.xml中,所以对所有的工程都有效,且它的<url-pattern>是配置在最前面的,这也说明了我们的请求如果没有配置自定义的Servlet来处理,那么就会由该Servlet来做第一个处理。

       好,既然已经理解了DefaultServlet的作用,那么现在来解决我们的第二个问题,静态资源请求如何处理才能避免404?

       在刚刚的前置知识中我们说过,任何一个请求,都至少需要一个Servlet来进行处理,如果第一个Servlet中没有进行请求转发或重定向操作,那么该请求就只会由匹配到的第一个Servlet处理后获取结果返回给客户端。 那么我们在SpringMVC中,当配置了前端控制器后,前端控制器的拦截路径和DefaultServlet的拦截路径相同,都是"/",这时候,我们的前端控制器会覆盖DefalutServlet,也就是前端控制器会处理掉所有的非".jsp"的请求,而DefaultServlet无发挥任何作用。
       然而,前端控制器的处理,要求必须有对应的控制器映射来匹配请求做后续操作,否则就报错。
       所以,我们的前端控制器是无法处理请求静态资源的客户端请求的(html静态资源的请求例外),只能把它交给DefaultServlet来做这个处理(这就叫术业有专攻)。
       但由于我们的前端控制器已经把DefalutServlet给覆盖了,所以为了能让我们使用DefaultServlet的功能,SpringMVC为我们提供了一个可以调用DefaultServlet来处理静态资源请求的DefaultServletHttpRequestHandler类。而我们只需要在SpringMVC的配置文件中注册该类,即可使用

	<!--开放对静态资源的访问-->
	<mvc:default-servlet-handler />

       这里要注意:这个DefaultServletHttpRequestHandler类并不是真正处理请求的Servlet,它是负责接收前端控制器处理不了的请求,并在其内部将请求转交给了Web容器(即Tomcat)的DefaultServlet去处理!所以真正处理请求的是Tomcat的DefaultServlet!

       此外,在SpringMVC配置文件中,除了注册这个handler外,还需要额外添加一个标签,用于开启mvc的注解驱动

	<!--开启mvc的注解驱动-->
	<mvc:annotation-driven />

注册后,在SpringMVC中,各个Servlet的工作流程如下:
       step1: 对于客户端发送的请求,如果是".jsp"请求,那么直接交给Tomcat服务器的JspServlet来进行处理;
       step2: 除此之外的其他请求,都会被前端控制器拦截并处理,前端控制器会去SpringIOC容器中找到控制层组件中的各个控制器方法,然后与该客户端请求进行一一匹配,当匹配到符合的控制器方法时,就调用该控制器方法对该客户端请求做进一步处理;
       step3: 如果没有任何一个控制器方法能够匹配到该请求,那么前端控制器会将该请求交给已经注册了的DefaultServlet来进行处理,由DefaultServlet去找到请求的资源并响应最终结果给客户端。

ps:Ⅰ、 以上的请求处理流程,对所有的请求都适用,不局限于静态资源请求,也就是说,非 “.jsp” 的请求会由DispatcherServlet和DefaultServlet先后负责处理,若两个Servlet都找不到请求所对应的资源,就会报错404。
       Ⅱ、当设置了<mvc:default-servlet-handler />标签开启DefaultServlet后,若找不到资源,是由Tomcat处理了该404错误,因此IDE的控制台是不会打印出错误信息的,但浏览器会显示404。若没有配置该标签,那么请求不经过DefaultServlet的处理,当在DispatcherServlet找不到匹配的控制器方法时就会由SpringMVC报404错误,而且还会在IDE的控制台打印错误信息报道“没有找到匹配的控制器方法”。
       Ⅲ、如果只配置了<mvc:default-servlet-handler />,那么浏览器的所有请求都会交给Tomcat的DefaultServlet处理,前端控制器和控制器方法全都失效,所以只有静态资源请求能成功处理。如果只配置了<mvc:annotation-driven />,那么浏览器的所有请求都会被前端控制器进行处理,DefaultServlet失效,所以静态资源请求会失败,其他类型的请求可以成功。因此,为保证所有类型的请求都能够被成功处理,这两个标签必须同时配置在SpringMVC的配置文件中。



       好了,以上就是我个人对本次内容的理解与解析,如果有什么不恰当的地方,还望各位兄弟在评论区指出哦。
       如果这篇文章对你有帮助的话,不妨点个关注吧~
       期待下次我们共同讨论,一起进步~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值