Spring boot 对于Spring MVC整合

Spring boot 对于Spring MVC整合

历史

Spring MVC 开发

web.xml

部署描述符

Servlet
Filter
Listener

位于WEB-INF目录下

DispatcherServlet

/*

ContextLoaderListener

jar war

Servlet3.0规范讲起(标准) 开始变化注解代替配置文件

1
2
3
4
5
6
<servlet>
<servlet-name>...
<servlet-class>...
</servlet>
<mapping />

@WebServlet(name=…)
@WebFilter
@WebListener

SPI

ServletContainerInitializer

ServletContainerInitializers (SCIs) are registered via an entry in the file META-INF/services/javax.servlet.ServletContainerInitializer that must be included in the JAR file that contains the SCI implementation.

Servletcontainerinitialalizer (SCIs)是通过在META-INF/services/javax.servlet.ServletContainerInitializer文件中的一个条目注册的。必须包含在JAR文件中包含SCI的实现。

SCI processing is performed regardless of the setting of metadata-complete. SCI processing can be controlled per JAR file via fragment ordering. If an absolute ordering is defined, the only those JARs included in the ordering will be processed for SCIs. To disable SCI processing completely, an empty absolute ordering may be defined.

SCI处理的执行与元数据完成的设置无关。SCI处理可以通过片段排序来控制每个JAR文件。如果定义了绝对排序,则仅对排序中包含的那些jar进行SCIs处理。为了完全禁用SCI处理,可以定义一个空的绝对排序。

SCIs register an interest in annotations (class, method or field) and/or types via the javax.servlet.annotation.HandlesTypes annotation which is added to the class.

SCIs通过添加到类中的javax.servlet.annotation.HandlesTypes注解注册(类、方法或字段)和/或类型。

onStartup(Set> c,ServletContext ctx)

Receives notification during startup of a web application of the classes within the web application that matched the criteria defined via the javax.servlet.annotation.HandlesTypes annotation.

符合HandlesTypes注解标准的类会在web应用程序启动期间接收到通知。

Params:
c – The (possibly null) set of classes that met the specified criteria
ctx – The ServletContext of the web application in which the classes were discovered

SpringServletContainerInitializer

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

Servlet 3.0 ServletContainerInitializer 被设计成基于代码的配置与传统的web.xml配置相反或者可以搭配来使用。

Mechanism of Operation

运行机制

This class will be loaded and instantiated and have its onStartup method invoked by any Servlet 3.0-compliant container during container startup assuming that the spring-web module JAR is present on the classpath. This occurs through the JAR Services API ServiceLoader.load(Class) method detecting the spring-web module’s META-INF/services/javax.servlet.ServletContainerInitializer service provider configuration file.

这个类将被加载和实例化,并在容器启动期间由任何Servlet 3.0兼容的容器调用它的onStartup方法(假设类路径上存在spring-web模块JAR)。这是通过JAR Services API ServiceLoader.load(Class)方法检测spring-web模块的META-INF/services/javax.servlet.ServletContainerInitializer服务提供者的配置文件。

In combination with web.xml

与web.xml搭配使用

A web application can choose to limit the amount of classpath scanning the Servlet container does at startup either through the metadata-complete attribute in web.xml, which controls scanning for Servlet annotations or through an element also in web.xml, which controls which web fragments (i.e. jars) are allowed to perform a ServletContainerInitializer scan. When using this feature, the SpringServletContainerInitializer can be enabled by adding “spring_web” to the list of named web fragments in web.xml as follows:

web应用程序可以选择限制类路径扫描Servlet container的数量在启动的时候,通过web.xml中的metada-complete属性,它控制扫描Servlet注解或通过元素也在web.xml,它控制允许哪些web片段(即jar)允许执行ServletContainerInitializer扫描。当使用这个特性时,SpringServletContainerInitializer可以通过在web.xml的命名web片段列表中添加“spring_web”来启用,如下所示:

1
2
3
4
<absolute-ordering>
<name>some_web_fragment</name>
<name>spring_web</name>
</absolute-ordering>

Relationship to Spring’s WebApplicationInitializer

Spring’s WebApplicationInitializer SPI consists of just one method: WebApplicationInitializer.onStartup(ServletContext). The signature is intentionally quite similar to ServletContainerInitializer.onStartup(Set, ServletContext): simply put, SpringServletContainerInitializer is responsible for instantiating and delegating the ServletContext to any user-defined WebApplicationInitializer implementations. It is then the responsibility of each WebApplicationInitializer to do the actual work of initializing the ServletContext. The exact process of delegation is described in detail in the onStartup documentation below.

Spring的WebApplicationInitializer SPI只包含一个方法:WebApplicationInitializer.onstartup (ServletContext)。这个签名与ServletContainerInitialize.ronStartup(Set, ServletContext)非常相似。:简单地说,SpringServletContainerInitializer负责将ServletContext实例化并委托给任何用户定义的WebApplicationInitializer实现。然后,每个WebApplicationInitializer的职责是执行初始化ServletContext的实际工作。下面的onStartup文档详细描述了委托的具体过程。

General Notes

In general, this class should be viewed as supporting infrastructure for the more important and user-facing WebApplicationInitializer SPI. Taking advantage of this container initializer is also completely optional: while it is true that this initializer will be loaded and invoked under all Servlet 3.0+ runtimes, it remains the user’s choice whether to make any WebApplicationInitializer implementations available on the classpath. If no WebApplicationInitializer types are detected, this container initializer will have no effect.

通常,这个类应该被看作是更重要的、面向用户的WebApplicationInitializer SPI的支持基础结构。利用这个容器初始化器也是完全可选的:虽然这个初始化器将在所有Servlet 3.0+运行时下加载和调用,但是是否在类路径上提供任何WebApplicationInitializer实现仍然是用户的选择。如果没有检测到WebApplicationInitializer类型,则此容器初始化器将不起作用。

Note that use of this container initializer and of WebApplicationInitializer is not in any way “tied” to Spring MVC other than the fact that the types are shipped in the spring-web module JAR. Rather, they can be considered general-purpose in their ability to facilitate convenient code-based configuration of the ServletContext. In other words, any servlet, listener, or filter may be registered within a WebApplicationInitializer, not just Spring MVC-specific components.

注意,这个容器初始化器和WebApplicationInitializer的使用并没有以任何方式“绑定”到Spring MVC上,除非类型是在Spring-web模块JAR中提供的。相反,它们可以被认为是通用的,因为它们能够方便地对ServletContext进行基于代码的配置。换句话说,任何servlet、监听器或过滤器都可以在WebApplicationInitializer中注册,而不仅仅是Spring特定于mvc的组件。

This class is neither designed for extension nor intended to be extended. It should be considered an internal type, with WebApplicationInitializer being the public-facing SPI.

这个类既不是为扩展设计的,也不打算扩展。它应该被认为是一个内部类型,WebApplicationInitializer是面向公共的SPI。

onStartup(@Nullable Set> webAppInitializerClasses, ServletContext servletContext)

Delegate the ServletContext to any WebApplicationInitializer implementations present on the application classpath.

将ServletContext委托给应用程序类路径上的任何WebApplicationInitializer实现。

Because this class declares @HandlesTypes(WebApplicationInitializer.class), Servlet 3.0+ containers will automatically scan the classpath for implementations of Spring’s WebApplicationInitializer interface and provide the set of all such types to the webAppInitializerClasses parameter of this method.

因为这个类声明了@HandlesTypes(WebApplicationInitializer.class), Servlet 3.0+容器将自动扫描类路径来实现Spring的WebApplicationInitializer接口,并将所有这些类型的集合提供给这个方法的webAppInitializerClasses参数。

If no WebApplicationInitializer implementations are found on the classpath, this method is effectively a no-op. An INFO-level log message will be issued notifying the user that the ServletContainerInitializer has indeed been invoked but that no WebApplicationInitializer implementations were found.

如果在类路径中没有找到WebApplicationInitializer实现,那么这个方法实际上是不需要操作的。将发出一条信息级别的日志消息,通知用户确实调用了ServletContainerInitializer,但是没有找到WebApplicationInitializer实现。

Assuming that one or more WebApplicationInitializer types are detected, they will be instantiated (and sorted if the @@Order annotation is present or the Ordered interface has been implemented). Then the WebApplicationInitializer.onStartup(ServletContext) method will be invoked on each instance, delegating the ServletContext such that each instance may register and configure servlets such as Spring’s DispatcherServlet, listeners such as Spring’s ContextLoaderListener, or any other Servlet API componentry such as filters.

假设检测到一个或多个WebApplicationInitializer类型,它们将被实例化(如果存在@@Order注释或已经实现了有序接口,则对它们进行排序)。ServletContext那么WebApplicationInitializer.onStartup()方法将被调用,每个实例委派ServletContext这样每个实例可能注册和配置Servlet如spring的DispatcherServlet,监听器如spring的ContextLoaderListener,或任何其他Servlet API过滤器等元件部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}

@HandlesTypes(WebApplicationInitializer.class)

This annotation is used to declare an array of application classes which are passed to a javax.servlet.ServletContainerInitializer.

此注解用于声明传递给javax.servlet.ServletContainerInitializer的应用程序类数组。

传统的Spring MVC 的容器初始化过程

  1. 通过SpringServletContainerInitializer来负责对容器启动时的相关组件进行初始化。
  2. 到底要初始化哪些组件则是通过Servlet规范中所提供的注解@HandlesTypes来指定的。
  3. 在SpringServletContainerInitializer中@HandlesTypes则明确指定为了WebApplicationInitializer.class类型。
  4. 在SpringServletContainerInitializer的onStartup方法中,则主要是完成了一些验证与组件装配的工作。