對springboot項目,可以通過兩種方式啓動服務,一是利用內嵌的Tomcat作爲web容器啓動,兩一種方式是藉助外部容器啓動。本章先介紹通過外部容器啓動的源碼流程。
一、springboot 藉助外部容器
@EnableSwagger2
@SpringBootApplication
public class Application extends SpringBootServletInitializer{
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
}
二、流程啓動原理
1、先看servlet3.0規範
- javax.servlet.ServletContainerInitializer是一個接口,web應用程序啓動的時候,會創建一個ServletContainerInitializer實例
- 提供ServletContainerInitializer的框架必須名爲javax.servlet的文件捆綁到jar文件的META-INF/services目錄中
- ServletContainerInitializer 實現上的handlesTypes註解,用於尋找感興趣的類–要麼是@HandlesTypes註解指定的類,要麼是其子類。
- ServletContainerInitializer實例的onStartup 方法將在應用程序啓動時且任何servlet偵聽器事件被激發之前被調用。
- ServletContainerInitializer 的onStartup 方法調用對象是(Set<Class<?>> webAppInitializerClasses),這些類要麼是initializer的擴展類,要麼是添加了@HandlesTypes註解的類,將會依次調用webAppInitializerClasses實例的onStartup方法。
對於spring框架,ServletContainerInitializer的綁定在spring-web jar包中,並給出了一個實現類org.springframework.web.SpringServletContainerInitializer
2、Tomcat容器啓動
在tomcat-core中,啓動的時候,會主動調用ServletContainerInitializer的onstartup方法:
public class StandardContext extends ContainerBase implements Context, NotificationEmitter{
private Map<ServletContainerInitializer,Set<Class<?>>> initializers =
new LinkedHashMap<>();
...
// 啓動
@Override
protected synchronized void startInternal() throws LifecycleException{
...
// Call ServletContainerInitializers 這裏會調用onstart方法
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry : initializers.entrySet()) {
try {
entry.getKey().onStartup(entry.getValue(),getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
}
...
}
3、查看SpringServletContainerInitializer方法的具體實現
// 感興趣的類型是WebApplicationInitializer
@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) {
// 非接口和虛類
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);
}
}
}
4、回到開始,在springboot中SpringBootServletInitializer的實現
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
protected Log logger;
private boolean registerErrorPageFilter = true;
public SpringBootServletInitializer() {
}
protected final void setRegisterErrorPageFilter(boolean registerErrorPageFilter) {
this.registerErrorPageFilter = registerErrorPageFilter;
}
// 此方法會被調用,並在所有的操作之前
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(this.getClass());
WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
public void contextInitialized(ServletContextEvent event) {
}
});
} else {
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
}
}
// 查看創建跟容器
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
// 創建builder
SpringApplicationBuilder builder = createSpringApplicationBuilder();
builder.main(getClass());
// 查詢是否存在根容器
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
// 創建初始化事件
builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
// 此方法被外部覆寫,所以會主動調用sources方法
builder = configure(builder);
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(getClass()));
}
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
}
// 這裏和直接用main函數沒有區別,後面介紹Main函數啓動方式
return run(application);
}
}
接着看是如何創建SpringApplication的,
public class SpringApplicationBuilder {
private final Set<Class<?>> sources = new LinkedHashSet<>();
// 這裏傳入了資源類App的類,帶着springboot的註解
public SpringApplicationBuilder sources(Class<?>... sources) {
this.sources.addAll(new LinkedHashSet<>(Arrays.asList(sources)));
return this;
}
// application add primarySources
public SpringApplication build(String... args) {
configureAsChildIfNecessary(args);
this.application.addPrimarySources(this.sources);
return this.application;
}
}
接着看SpringApplication.run()
// 這裏會創建一個容器並刷新
public ConfigurableApplicationContext run(String... args) {
// 簡單計時
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 1. 獲取並啓動個監聽器,每一次listeners.XXX()方法調用,都將會廣播對應事件給applicationListeners監聽器處理
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 構造容器環境
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
// 設置需要忽略的bean
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 創建根容器
context = createApplicationContext();
// 啓動錯誤報告實例,用來報告錯誤
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 準備容器
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
// 刷新容器
refreshContext(context);
// 刷新容器擴展接口
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 這裏是對容器後加入的一些listener啓動
listeners.started(context);
//調用CommandLineRunner和ApplicationRunner的run方法
callRunners(context, applicationArguments);
}catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
創建listener的實現
// args可爲空
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
// 自動加載
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 加載SpringApplicationRunListener的所有Listener,這個是加載classPath下的listener
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
// 創建Listener的實例
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}