Spring 是如何支持 Servlet 3.0 的?

写在前面:

ServletContainerInitializer 也是 Servlet 3.0 新增的一个接口,容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 ServletContainerInitializer 的实现类,并且容器将 WEB-INF/lib 目录下 JAR 包中的类都交给该类的 #onStartup(...) 方法处理,我们通常需要在该实现类上使用 @HandlesTypes 注解来指定希望被处理的类,过滤掉不希望给 #onStartup(...) 处理的类。

正文:

回到我们的 spring 全家桶,可能已经忘了具体是什么时候开始不写 web.xml 了,我只知道现在的项目已经再也看不到它了,spring 又是如何支持 servlet3.0 规范的呢?

寻找 spring 中 ServletContainerInitializer 的实现类并不困难,可以迅速定位到 org.springframework.web.SpringServletContainerInitializer 该实现类。

查看其 java doc,描述如下:

Servlet 3.0 {@link ServletContainerInitializer} designed to support code-based configuration of the servlet container using Spring’s {@link WebApplicationInitializer} SPI as opposed to (or possibly in combination with) the traditional {@code web.xml}-based approach.

注意我在源码中标注两个序号,这对于我们理解 spring 装配 servlet 的流程来说非常重要。

  1. <1> 英文注释是 spring 源码中自带的,它提示我们由于 servlet 厂商实现的差异,onStartup 方法会加载我们本不想处理的 class,所以进行了特判。另外,也要注意下 @HandlesTypes(WebApplicationInitializer.class) 注解,如果厂商正确的实现 @HandlesTypes 的逻辑,传入的 Set<Class<?>> webAppInitializerClasses 都是 WebApplicationInitializer 对象。
  2. <2> spring 与我们之前的 demo 不同,并没有在 SpringServletContainerInitializer 中直接对 servlet 和 filter 进行注册,而是委托给了一个陌生的类 org.springframework.web.WebApplicationInitializer 。WebApplicationInitializer 类便是 spring 用来初始化 web 环境的委托者类,它通常有三个实现类:

 

你一定不会对 DispatcherServlet 感到陌生,AbstractDispatcherServletInitializer#registerDispatcherServlet 便是无 web.xml 前提下创建 DispatcherServlet 的关键代码。代码如下:

<1> 处,调用 #createServletApplicationContext() 方法,创建 WebApplicationContext 对象。代码如下:

  1. 该方法由子类 AbstractAnnotationConfigDispatcherServletInitializer 重写,并且创建的 WebApplicationContext 的子类 AnnotationConfigWebApplicationContext 对象。

<2> 处,调用 #createDispatcherServlet(WebApplicationContext servletAppContext) 方法,创建 FrameworkServlet 对象。代码如下:

  1. 创建 FrameworkServlet 的子类 DispatcherServlet 对象。
  2. 另外,比较有趣的是传入的 servletAppContext 方法参数,这就是该 DispatcherServlet 的 Servlet WebApplicationContext 容器

然后,我们可以去项目中寻找一下 org.springframework:spring-web:version 的依赖,它下面就存在一个 ServletContainerInitializer 的扩展,指向了 SpringServletContainerInitializer,这样只要在 servlet 3.0 环境下部署,spring 便可以自动加载进行初始化:

注意,上述这一切特性从 spring 3 就已经存在了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章