SpringBoot 使用外部 Tomcat 容器运行
继承 SpringBootServletInitializer 类,重写 configure 方法
@SpringBootApplication public class SpisApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(SpisApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(SpisApplication.class); } }
原理
servlet 部分
从 servlet 3.0 开始,Tomcat 启动时会自动加载实现了 ServletContainerInitializer 接口的类(需要在 META-INF/services 目录下新建配置文件)。也称为 SPI 机制
package javax.servlet; import java.util.Set; public interface ServletContainerInitializer { public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException; }
spring 部分
SpringServletContainerInitializer 的 onStartup 会被执行,webAppInitializerClasses 参数是所有实现了 WebApplicationInitializer 接口的类(servlet 注解 @HandlesTypes(WebApplicationInitializer.class) 的作用)
package org.springframework.web; import java.lang.reflect.Modifier; import java.util.LinkedList; import java.util.List; import java.util.ServiceLoader; import java.util.Set; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.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 = new LinkedList<>(); if (webAppInitializerClasses != null) { for (Class<?> waiClass : webAppInitializerClasses) { 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); } } }
再看 SpringBoot 启动类所需要继承的类
public abstract class SpringBootServletInitializer implements WebApplicationInitializer
就是这个接口,所以 Tomcat 启动时 SpringBoot 也就跟着启动了
public interface WebApplicationInitializer { void onStartup(ServletContext servletContext) throws ServletException; }
附上利用 Servlet 该特性的 SSM 整合:https://www.cnblogs.com/jhxxb/p/10512553.html
SpringBoot 内嵌 Tomcat 的启动不是 SPI 机制,是直接启动的。
https://spring.io/blog/2014/03/07/deploying-spring-boot-applications