spring boot war包啟動原理


spring boot用了小兩年,war包發布也用過多次了。今天糾結了下它的啟動原理,網上查了下資料,感覺不夠精簡,於是自己總結了一下。
 
實現
spring boot項目打war包很簡單,項目main application class繼承 SpringBootServletInitializer,pom文件中packaging配置為war即可。

spring boot main application class

@SpringBootApplication
public class Application extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

 

原理
簡而言之,spring boot war啟動是利用Servlet 3.0新增的ServletContainerInitializer接口結合SPI(Service Provider Interface)機制實現的。
 主要邏輯
  1. Spring在spring-web-version.jar/META-INF/services/javax.servlet.ServletContainerInitializer文件中,配置了spring對ServletContainerInitializer接口的實現類org.springframework.web.SpringServletContainerInitializer。
  2. Servlet Container啟動階段掃描jar包中META-INF/services/javax.servlet.ServletContainerInitializer文件, 獲取ServletContainerInitializer實現類並實例化,解析ServletContainerInitializer上@HandlesTypes注解,查找出@HandlesTypes限定的類型集合,作為ServletContainerInitializer.onStartup 方法處理的第一個參數c。
  3. Servlet Container依次調用每個ServletContainerInitializer實例的onStartup。war包啟動的場景中會調用SpringServletContainerInitializer.onStartup方法,該方法循環調用c集合中每個 WebApplicationInitializer子類(即SpringBootServletInitializer)的onStartup方法。
  4. SpringBootServletInitializer.onStartup方法調用SpringBootServletInitializer.createRootApplicationContext方法,createRootApplicationContext方法中構建SpringApplication並執行SpringApplication.run方法以啟動整個spring項目。

關鍵源碼

Spring的ServletContainerInitializer實現類

@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) {
                   // 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);
          }
     }
}

 

WebApplicationInitializer.class接口的實現類

public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
......
     @Override
     public void onStartup(ServletContext servletContext) throws ServletException {
          // Logger initialization is deferred in case an ordered
          // LogServletContextInitializer is being used
          this.logger = LogFactory.getLog(getClass());
          WebApplicationContext rootAppContext = createRootApplicationContext(servletContext);
          ......//略去部分代碼
     }

     protected WebApplicationContext createRootApplicationContext(
              ServletContext servletContext) {
          SpringApplicationBuilder builder = createSpringApplicationBuilder();
          builder.main(getClass());
          ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
          ......//略去部分代碼
          return run(application);
     }

    protected WebApplicationContext run(SpringApplication application) {
          return (WebApplicationContext) application.run();
     }

}

 

轉載請注明出處,尊重彼此的勞動成果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM