LoginSignup
3
1

More than 1 year has passed since last update.

SpringBootServletInitializerがTomcatで実行される仕組み

Last updated at Posted at 2022-11-06

概要

Spring BootアプリをTomcatにデプロイして実行する場合、SpringBootServletInitializerを継承したクラスを作成することで実現するが、Servlet仕様でもないSpringBootServletInitializerを継承したクラスがなぜTomcatで実行されるのかについて述べる。

Servlet仕様との紐づけ

ずばりSpring Webに含まれるServletContainerInitializerを実装したSpringServletContainerInitializerの仕業だ。以下がそのクラスだ。

SpringServletContainerInitializer.java
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のものに変更されている。

SpringServletContainerInitializer.java(Spring Web 5.3.x)
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
SpringServletContainerInitializer.java(Spring Web 6.0.0-RC2)
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以前でなければ正常に動作しない。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1