Spring Boot「14」MVC 与前端控制器模式
[Spring Boot] Spring MVC and Front Controller
Spring MVC 采用典型的 Front Controller 架构。DispatcherServlet 作为其中的 Front Controller 负责将用户请求匹配到对应的 Controller 中,并将 Controller 返回的数据交给 ViewResolver 根据不同的模板引擎(例如 JSP、Thymeleaf 等)渲染出响应的 View 返回给用户。下图源自于baeldung,详细描述了 Spring MVC 的具体工作流程:
01-基础概念
在学习 Spring MVC 时,与 Servlet 相关地诸多概念让初学者非常的头疼。本节中就对这些基本概念进行一下梳理,帮助各位更好地理解这些概念。
01.1-Servlet
首先,第一个概念就是 Servlet。简单来说,Servlet 就是一个 Java 类,它能够处理请求,并返回一个响应。它的接口定义是:
init\destroy\service 是 Servlet 整个声明周期中三个比较重要的函数。其中 service 就是其处理 request 并返回 response 的接口。
Spring MVC 中最重要的一个 Servlet 实现就是 DispatcherServlet。
01.2-Servlet 容器
Servlet 运行在 Servlet 容器中,由容器管理。Servlet 容器是 Web 服务器或应用服务器的一部分,为 Servlet 提供运行时上下文,例如网络服务、MIME 解析等;Servlet 容器可以运行在 Web 服务器所在的进程中,也可以是同一主机上的不同进程,甚至可以是不同主机上的进程。当 Web 服务器收到 Request 请求时,会转交给 Servlet 容器。之后,Servlet 容器会选择一个 Servlet 来处理请求,并将响应返回给 Web 服务器。
01.3-ServletContext
ServletContext 定义了 Web 应用程序的 servlet 视图。以下为 Servlet Specification 对 ServletContext 的描述:
The ServletContext interface defines a servlet's view of the Web application within which the servlet is running.
怎么来理解这句话呢?一个 Web 应用中所定义的所有的 Servlet 都是运行在某个 ServletContext 下的。而且,每个 ServletContext 都关联了一个路径,称之为 context path。Servlet 容器会根据请求的 URL 来选择与其匹配的 ServletContext,并将请求交与其处理。
与 context path 相对应的,ServletContext 中的所有 Servlet 也都关联了一个路径,称之为 servlet path。它是相对于 context path 的一个相对路径。举例说明,如果我们的应用关联的 context path 为 /demo,并且有一个 Servlet 且 它的 servlet path 为 /hi。那么,对 URL 为 /demo/hi 的请求将由此 Servlet 处理。
当我们在 Tomcat 中部署 Web 应用时,需要增加配置文件 conf/Catalina/localhost/${web-app-name}.xml,且里面的内容与下面类似:
docBase 指定了我们 Web 应用所在的磁盘位置,path 就是应用对应的 ServletContext 的 context path。
如何定义一个应用的 context path 呢?有如下几种方式:
以 Property 的方式,在 application.properties 或 application.ymal 中指定:server.servlet.context-path=/demo
通过环境变量的方式指定
(2.x) export SERVER_SERVLET_CONTEXT_PATH=/demo unix | set SERVER_SERVLET_CONTEXT_PATH=/baeldung windows
(1.x) SERVER_CONTEXT_PATH
通过编程方式指定:
(2.x) WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
(1.x) EmbeddedServletContainerCustomizer
如何向 ServletContext 中注册一个 Servlet 呢?
在 Jakarta EE 中,可以通过:
web.xml 中的
<servlet></servlet>
标签@WebServlet
注解在 Spring Boot 中,可以通过:
web.xml 中的
<servlet></servlet>
标签编程方式指定: WebApplicationInitializer & WebMvcConfigurer | ServletRegistrationBean
通过 Property 指定:
servlet.name=dispatcherExample & servlet.mapping=/dispatcherExampleURL
System.setProperty("custom.config.location", "classpath:custom.properties"); & System.getProperty("custom.config.location");
spring.mvc.servlet.path=/test
DispatcherServlet
在 Spring MVC 中,DispatcherServlet 是应用的入口,负责将 HttpRequests 定向到 handler。HandlerAdapter 接口主要是为了方便处理 HttpServletRequest。
supports 用来判断是否支持某个 handler 实例;handle 方法接收 request & response & handler 对象,返回一个 ModelAndView 对象,后续会被 DispatcherServlet 处理。调用 DispatcherServlet#getHandler 时,每个实现了 HandlerAdapter 接口的对象会被放置到 HandlerExecutionChain 中,随着处理过程的推进,它们的 handle() 方法会被应用到 HttpServletRequest 上。
常用的 HandlerAdapter 实现:
SimpleControllerHandlerAdapter & BeanNameUrlHandlerMapping,负责将 request 定向到实现了 Controller 接口的实例中。同样是 Spring MVC 中默认的 HandlerAdapter。
SimpleServletHandlerAdapter,负责将 DispatcherServlet 收到的 request 定向到其他实现了 Servlet 接口的实例,调用其 service() 方法。
(deprecated in Spring 3.2)AnnotationMethodHandlerAdapter & DefaultAnnotationHandlerMapping,负责将 request 定向到标注了
@RequestMapping
注解的实例中。RequestMappingHandlerAdapter & RequestMappingHandlerMapping,将 request 定向到
@RequestMapping
注解的方法。@EnableWebMvc
和<mvc:annotation-driven />
会自动向容器中注入这两个 bean。HttpRequestHandlerAdapter,负责处理 HttpRequests,将 request 定向到实现了 HttpRequestHandler 接口的实例中,调用其 handleRequest() 方法,返回值为 void 而非 ModelAndView。一般用在不需要渲染视图的场景。
除上述部分外,Spring MVC 中还包括如下组件:
DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE 关联到一个 WebApplicationContext
DispatcherServlet 通过 getHandler 方法获得所有实现了 HandlerAdapter 接口的对象,HandlerAdapter 需要与 HandlerMapping 一起使用。
(可选)ViewResolver,主要通能:决定提供何种类型的视图,以及从哪里获取对应的视图。
(可选)LocaleResolver,主要功能:customize session, request, or cookie information
(可选)ThemeResolver,主要功能:视图主题相关
(可选)MultipartResolver,主要功能:将 request 包装成为 MultipartHttpServletRequest
HandlerExceptionResolver,主要功能:Spring 3.2 之前统一异常处理方式
版权声明: 本文为 InfoQ 作者【Samson】的原创文章。
原文链接:【http://xie.infoq.cn/article/a91b4c7ba13cad60d4485ebe5】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论