介绍
本系列文章将从Spring Boot的main函数入口,一步一步带领大家阅读spring boot的源代码,并且会详细解释spring boot各个类和方法上注释的含义,帮助阅读英文有困难的同学更好的理解spring boot的原理,话不多说,直接开始。
程序入口
通常在一个spring boot的应用中,会看到下面一段代码作为应用的入口
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}那么这段代码究竟做了什么呢,让我们深入来分析它背后的原理。当我们点击run来查看源代码时,会看到下面这段代码,这段Java Doc说明这是一个助手方法,可以通过指定一个primarySource的source源来启动,这个primarySource其实就是我们的启动类Application
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}接着,我们继续进入run方法,你会看到另外一个helper方法,这个helper方法首先初始化一个SpringApplication,然后再一次执行SpringApplication实例的run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}初始化SpringApplication
我们将一行一行逐步讲解初始化SpringApplication的逻辑
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}初始化resourceLoader
resourceLoader 这里为空,当这个resourceLoader为空的时候,spring boot会使用缺省classLoader,这个resrouceLoader的作用后面会另外写一篇文章详细讲解,这里不影响我们对整体逻辑的理解,先跳过。
初始化webApplicationType
webApplicationType确定当前应用的类型,进入deduceFromClasspath()可以看到spring boot是如何确定类型的,从下面这段代码中可以看出当前应用属于哪一种类型取决于classpath中是否加载到了相应的类。
- 如果classpath中有
org.springframework.web.reactive.DispatcherHandler类,但是没有DispatcherServlet和ServletContainer类,则类型为Reactive web application,会启动一个嵌入式的reactive web server。 - 如果classpath中既没有
javax.servlet.Servlet又没有org.springframework.web.context.ConfigurableWebApplicationContext,那么该应用不是一个web application。 - 如果不是以上两种,那么就判断为一个servlet的web application,会启动一个嵌入式的servlet web server。
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}初始化BootstrapRegistryInitializer
我们再来看bootstrapRegistryInitializers,下面的代码简单来说就是通过反射加载在META-INF/spring.factories中所有的 bootstrapRegistryInitializer ,具体加载过程会在后续文章中详解
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}而BootstrapRegistryInitializer又是干什么的呢,我们为什么要加载BootstrapRegistryInitializer呢,我们从Java Doc里就可以明白这个接口是通过Initialize的方法来加载BootstrapRegistry的。
/**
* Callback interface that can be used to initialize a {@link BootstrapRegistry} before it
* is used.
*
* @author Phillip Webb
* @since 2.4.5
* @see SpringApplication#addBootstrapRegistryInitializer(BootstrapRegistryInitializer)
* @see BootstrapRegistry
*/
@FunctionalInterface
public interface BootstrapRegistryInitializer {
/**
* Initialize the given {@link BootstrapRegistry} with any required registrations.
* @param registry the registry to initialize
*/
void initialize(BootstrapRegistry registry);
}可能又有同学要问,什么是 BootstrapRegistry,我们继续看Java Doc,继承这个注册接口的类会在ApplicationContext准备好之前可用,并且接口上有一个register的方法和一个addCloseListener
register: 以class为key来注册一些对象的实例来使用addCloseListener: 在BootstrapContext关闭和ApplicationContext准备好之前调用该Listener
那么我们可以简单理解为这个BootstrapRegistry是为了让spring boot在 ApplicationContext准备好之前能有机会来注册一些类,并且通过触发ApplicationContext ready的事件来处理一些逻辑
/**
* A simple object registry that is available during startup and {@link Environment}
* post-processing up to the point that the {@link ApplicationContext} is prepared.
* <p>
* Can be used to register instances that may be expensive to create, or need to be shared
* before the {@link ApplicationContext} is available.
* <p>
* The registry uses {@link Class} as a key, meaning that only a single instance of a
* given type can be stored.
* <p>
* The {@link #addCloseListener(ApplicationListener)} method can be used to add a listener
* that can perform actions when {@link BootstrapContext} has been closed and the
* {@link ApplicationContext} is fully prepared. For example, an instance may choose to
* register itself as a regular Spring bean so that it is available for the application to
* use.
*
* @author Phillip Webb
* @since 2.4.0
* @see BootstrapContext
* @see ConfigurableBootstrapContext
*/
public interface BootstrapRegistry {
/**
* Register a specific type with the registry. If the specified type has already been
* registered and has not been obtained as a {@link Scope#SINGLETON singleton}, it
* will be replaced.
* @param <T> the instance type
* @param type the instance type
* @param instanceSupplier the instance supplier
*/
<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplie
/**
* Add an {@link ApplicationListener} that will be called with a
* {@link BootstrapContextClosedEvent} when the {@link BootstrapContext} is closed and
* the {@link ApplicationContext} has been prepared.
* @param listener the listener to add
*/
void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener);
//....
}初始化ApplicationContextInitializer
这里和初始化bootstrapRegistryInitializers的逻辑一致,通过getSpringFactoriesInstances最终获得META-INF/spring.factories中所有的ApplicationContextInitializer,而ApplicationContextInitializer是用来初始化ConfigurableApplicationContext,关于ConfigurableApplicationContext将在后续篇幅中介绍
初始化ApplicationListener
这里逻辑也和初始化bootstrapRegistryInitializers一致,通过getSpringFactoriesInstances最终获得META-INF/spring.factories中所有的ApplicationListener
deduceMainApplicationClass
这个方法比较巧妙,通过构造一个RuntimeException,然后得到StackTrace的方法,来获得main方法所在的类,这样做的原因是因为primarySource是作为数组形式传入方法的,如果有超过一个primarySource,那么就没法直接从primarySource来判断哪个source是main方法所在实例。
this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}结语
由于篇幅有限,我们将在后续文章中继续从下面的run方法开始讲解Spring Boot的后续加载过程。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}