Боевой анализ SpringBoot — развертывание Tomcat

задняя часть контейнер Tomcat встроенный

предисловие

существуетПервый опыт Spring BootВ этой статье мы научились быстро запускатьSpring Bootпрограмма, иSpring BootТакже поддерживаются традиционные методы развертывания: упакуйте проект какWAR, затем поWebСервер загружается и запускается, на этот раз сTomcatНапример, давайте быстро научимся использоватьWARспособ развертыванияSpring Bootпроект, код размещен наGithubи проведите простой анализ исходного кода.

текст

использоватьSpring Initializrбазовая загрузка инструментаSpring Bootтехника, выборMavenспособ сборки, версия официальная версия 1.5.16, только выберите одинWebполагаться.

image-20181014094106403

наследоватьSpringBootServletInitializerнагрузка

После открытия загруженного проекта запустите классSpringbootTomcatApplicationизменять, наследоватьSpringBootServletInitializerЭтот абстрактный класс и переопределить метод родительского классаSpringApplicationBuilder configure(SpringApplicationBuilder builder) .

@SpringBootApplication
public class SpringbootTomcatApplication extends SpringBootServletInitializer  {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
	    return builder.sources(SpringbootTomcatApplication.class);
	}

	public static void main(String[] args) {
		SpringApplication.run(SpringbootTomcatApplication.class, args);
	}
}

SpringBootServletInitializerкласс будет вServletКогда контейнер запускает программу, это позволяет нам настроить конфигурацию программы, и здесь нам нужно будет сделатьServletЭтот класс загружается, когда контейнер запускает программу.

Измените метод упаковки на WAR

следующий вpom.xmlВ файле измените метод упаковки наWAR,ПозволятьMavenпри построении сWARспособ генерировать.

<groupId>com.one</groupId>
<artifactId>springboot-tomcat</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>

Также обратите внимание, что: Для обеспечения встроенногоservletКонтейнер не влияет на контейнер сервлета, который развертывает файл войны, вот онTomcat. Нам также необходимо встроитьservletЗависимости контейнера отмечены какprovided.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-tomcat</artifactId>
	<scope>provided</scope>
</dependency>

Реализовать обработку запросов на отдых

чтобы доказатьWARЕсли развертывание прошло успешно, мы реализуем самую базовую обработкуWebДля требуемой функциональности добавьте некоторые в класс запускаSpring MVCкод

@SpringBootApplication
@RestController
public class SpringbootTomcatApplication extends SpringBootServletInitializer  {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
	    return builder.sources(SpringbootTomcatApplication.class);
	}

	public static void main(String[] args) {
		SpringApplication.run(SpringbootTomcatApplication.class, args);
	}


	@RequestMapping(value = "/")
	public String hello() {
		return "hello tomcat";
	}
}

Упаковка проекта

Готов упаковать сейчасSpring Bootзапрограммировать вWAR, тогда пустьTomcatСервер загружен, используйте команду сборки в текущем пути проекта

mvn clean package

ПоявлятьсяBUILD SUCCESSзначит упаковка удалась

image-20181014101039797

Затем вы можете проецироватьtargetСм. сгенерированный каталог вWAR.

image-20181014101142382

Развернуть Tomcat

будетspringboot-tomcat-0.0.1-SNAPSHOT.warПоместите его в папку программы Tomcat**webapps**вниз, затем бегитеTomcat, после успешного запуска можно войти в браузереhttp://localhost:8080/springboot-tomcat-0.0.1-SNAPSHOT/, запросите этот простойWebпрограмма.

image-20181014101753493

сюда,WARспособ развертыванияSpring BootПрограмма завершена 🎉🎉🎉

Анализ исходного кода

После завершения этого у меня не может не возникнуть вопрос: почему унаследовалSpringBootServletInitializerclass и переопределить его метод configure, чтобы развернуть его в режиме войны?С вопросом мы ищем ответ с точки зрения исходного кода.

Установите точку останова в методе, переопределяемом классом запуска SpringbootTomcatApplication, и просмотрите процесс вызова метода, когда Tomcat запускает проект.

Запустите проект в режиме отладки, когда вы дойдете до этой строки кода, вы увидите два важных класса.SpringBootServletInitializerа такжеSpringServletContainerInitializer .

image-20181014131101858

Как видно из рисунка вызов метода configure находится в родительском классеcreateRootApplicationContext, конкретный код выглядит следующим образом, некритические части опущены, а важные закомментированы.

protected WebApplicationContext createRootApplicationContext(
			ServletContext servletContext) {
		SpringApplicationBuilder builder = createSpringApplicationBuilder(); //  新建用于构建SpringApplication 实例的 builder
		builder.main(getClass());
		// ....
		builder.initializers(
				new ServletContextApplicationContextInitializer(servletContext));
		builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
		builder = configure(builder); // 调用子类方法,配置当前 builder
		builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
		SpringApplication application = builder.build(); // 构建 SpringApplication 实例
		if (application.getSources().isEmpty() && AnnotationUtils
				.findAnnotation(getClass(), Configuration.class) != null) {
			application.getSources().add(getClass());
		}
    	//...
		return run(application);  // 运行 SpringApplication 实例
	}

SpringApplicationBuilderЭкземпляры должны следовать шаблону проектирования компоновщика для завершенияSpringApplicationпостроить сборку.

а такжеcreateRootApplicationContextВызов метода по-прежнему выполняется в этом классе, который более знаком, поскольку традиционныйSpring WebЗапуск проекта также создаетWebApplicationContextпример.

@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		// Logger initialization is deferred in case a ordered
		// LogServletContextInitializer is being used
		this.logger = LogFactory.getLog(getClass());
		WebApplicationContext rootAppContext = createRootApplicationContext(
				servletContext); // 创建一个 WebApplicationContext 实例.
        // ...
	}

Вот опять проблемаonStartupКак реализуется метод?SpringServletContainerInitializerПоявляется класс.

image-20181014133828708

SpringServletContainerInitializerреализация классаServlet 3.0нормативныйServletContainerInitializerинтерфейс, что означает, что когдаServletКогда контейнер запустится, он вызоветServletContainerInitializerинтерфейсonStartupМетоды уведомляют классы, реализующие этот интерфейс.

public interface ServletContainerInitializer {
    void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

Теперь давайте посмотримSpringServletContainerInitializerизonStarupКонкретная реализация метода выглядит следующим образом, в ключевой строке кода 23 ~ 24.initializersЯвляетсяLinkedListколлекция, со всеми реализациямиWebApplicationInitializerЭкземпляры интерфейса, перебор здесь вызовет соответствующийonStartupпередача методаServletContextпример для завершенияWebУведомление о запуске сервера.

@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
      throws ServletException {
   List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
   if (webAppInitializerClasses != null) {
      for (Class<?> waiClass : webAppInitializerClasses) {
         if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
               WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
            try {
                // 提取webAppInitializerClasses集合中 实现 WebApplicationInitializer 接口的实例
               initializers.add((WebApplicationInitializer) waiClass.newInstance());
            }
            catch (Throwable ex) {
               throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
            }
         }
      }
   }
   // ...
    
   for (WebApplicationInitializer initializer : initializers) {
      initializer.onStartup(servletContext); // 调用所有实现 WebApplicationInitializer 实例的onStartup 方法
   }
}

отследить выполнение доSpringServletContainerInitializerВ строке 22 класса мы видим, что коллекция содержит наш класс запуска, поэтому мы, наконец, вызываем его родительский классonStartupметод завершенWebApplicationContextсоздание экземпляра.

image-20181014135435492

Увидев это, давайте подытожим процессы вызова этих классов и разберем их.Spring BootпрограммаWARСпособ запуска процесса:

SpringServletContainerInitializer#onStartup

=>SpringBootServletInitializer#onStartup

=> ``SpringBootServletInitializer#createRootApplicationContext​ =>SpringbootTomcatApplication#configure`

Кроме того, я также получил один балл: при выполненииSpringBootServletInitializerизcreateRootApplicationContextВ конце метода вызовитеrun(application).

Это также показывает, что когдаWARспособ развертыванияSpring Bootпроект, фиксированный сгенерированныйMainМетод больше не будет выполняться и может быть удален.

//当项目以WAR方式部署时,这个方法就是无用代码
public static void main(String[] args) {
	SpringApplication.run(SpringbootTomcatApplication.class, args);
}

Эпилог

Эта статья в основном учит, как сделатьSpring BootкWARспособ начать и выполнить простой анализ исходного кода, чтобы помочь нам лучше понятьSpring Boot.Надеюсь, это поможет, в дальнейшем будет больше реальных боев и анализов, так что следите за обновлениями.

Ссылаться на