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)機制實現的。
主要邏輯
-
Spring在spring-web-version.jar/META-INF/services/javax.servlet.ServletContainerInitializer文件中,配置了spring對ServletContainerInitializer接口的實現類org.springframework.web.SpringServletContainerInitializer。
-
Servlet Container啟動階段掃描jar包中META-INF/services/javax.servlet.ServletContainerInitializer文件, 獲取ServletContainerInitializer實現類並實例化,解析ServletContainerInitializer上@HandlesTypes注解,查找出@HandlesTypes限定的類型集合,作為ServletContainerInitializer.onStartup 方法處理的第一個參數c。
-
Servlet Container依次調用每個ServletContainerInitializer實例的onStartup。war包啟動的場景中會調用SpringServletContainerInitializer.onStartup方法,該方法循環調用c集合中每個 WebApplicationInitializer子類(即SpringBootServletInitializer)的onStartup方法。
-
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(); } }
轉載請注明出處,尊重彼此的勞動成果。
