предисловие
существуетПервый опыт Spring BootВ этой статье мы научились быстро запускатьSpring Bootпрограмма, иSpring BootТакже поддерживаются традиционные методы развертывания: упакуйте проект какWAR, затем поWebСервер загружается и запускается, на этот раз сTomcatНапример, давайте быстро научимся использоватьWARспособ развертыванияSpring Bootпроект, код размещен наGithubи проведите простой анализ исходного кода.
текст
использоватьSpring Initializrбазовая загрузка инструментаSpring Bootтехника, выборMavenспособ сборки, версия официальная версия 1.5.16, только выберите одинWebполагаться.
наследовать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значит упаковка удалась
Затем вы можете проецироватьtargetСм. сгенерированный каталог вWAR.
Развернуть Tomcat
будетspringboot-tomcat-0.0.1-SNAPSHOT.warПоместите его в папку программы Tomcat**webapps**вниз, затем бегитеTomcat, после успешного запуска можно войти в браузереhttp://localhost:8080/springboot-tomcat-0.0.1-SNAPSHOT/, запросите этот простойWebпрограмма.
сюда,WARспособ развертыванияSpring BootПрограмма завершена 🎉🎉🎉
Анализ исходного кода
После завершения этого у меня не может не возникнуть вопрос: почему унаследовалSpringBootServletInitializerclass и переопределить его метод configure, чтобы развернуть его в режиме войны?С вопросом мы ищем ответ с точки зрения исходного кода.
Установите точку останова в методе, переопределяемом классом запуска SpringbootTomcatApplication, и просмотрите процесс вызова метода, когда Tomcat запускает проект.
Запустите проект в режиме отладки, когда вы дойдете до этой строки кода, вы увидите два важных класса.SpringBootServletInitializerа такжеSpringServletContainerInitializer .
Как видно из рисунка вызов метода 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Появляется класс.
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создание экземпляра.
Увидев это, давайте подытожим процессы вызова этих классов и разберем их.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.Надеюсь, это поможет, в дальнейшем будет больше реальных боев и анализов, так что следите за обновлениями.