SpringBootServletInitializer是在Tomcat中执行的机制

总结

如果要将Spring Boot应用程序部署到Tomcat并执行,可以通过创建一个继承SpringBootServletInitializer的类来实现。然而,为什么继承SpringBootServletInitializer并非Servlet规范的类也能在Tomcat上执行,现在来解释一下。

与Servlet规范的关联

就是Spring Web中实现了ServletContainerInitializer的SpringServletContainerInitializer所做的。以下是该类的定义。

package org.springframework.web;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;

import jakarta.servlet.ServletContainerInitializer;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.HandlesTypes;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = Collections.emptyList();

		if (webAppInitializerClasses != null) {
			initializers = new ArrayList<>(webAppInitializerClasses.size());
			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);
		}
	}

}

实现了ServletContainerInitializer的类的onStartup方法将在Tomcat部署时执行。
该方法首先查找并将实现了WebApplicationInitializer接口的类存储在列表中,然后按顺序依次执行它们的onStartup方法。执行顺序按照Spring的@Order注解指定的顺序排列。
由于SpringBootServletInitializer实现了WebApplicationInitializer接口,因此会执行其onStartUp方法,创建Spring的ApplicationContext,准备DI容器等,从而启动Spring Boot应用程序。

请注意Spring Boot和Tomcat的版本

从Spring Boot 3.0.0开始,由于依赖的Spring Web版本升级到6,SpringServletContainerInitializer使用的ServletContainerInitializer已经从Java EE切换至Jakarta EE。

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import jakarta.servlet.ServletContainerInitializer;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.HandlesTypes;

因此,使用Spring Boot 3.0.0创建的war文件只能在Tomcat 10.0.x及以上版本上正常运行。反之,使用Spring Boot 2.7.x及之前版本创建的war文件只能在Tomcat 9.x及之前版本上正常运行。

广告
将在 10 秒后关闭
bannerAds