Spring Boot启动流程源码分析

注解

  在Spring Boot入口类里,会在类名上加@EnableAutoConfiguration或者@SpringBootApplication注解,这两个有什么差别呢?

@EnableAutoConfiguration

@EnableAutoConfiguration注解的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}

@SpringBootApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}

  可以看出@SpringBootApplication相当于是@Configuration,@EnableAutoConfiguration,@ComponentScan这三者的结合。@SpringBootApplication也提供了后面两个注解配置的别名(alias)。
例如exclude排除的类,scanBasePackages扫描的包名等,这些都可以直接该注解上进行配置。

程序入口类SpringApplication

1
2
3
4
5
6
7
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
public SpringApplication(Object... sources) {
initialize(sources);
}

  Spring Boot入口程序很简单,直接通过静态的SpringApplication.run就运行了,返回ConfigurableApplicationContext对象。
  进入run方法里,可以很直观看到,启动程序主要做两部分,一个是initialize()初始化所有配置参数,另外一个是run()方法。

initialize()

1
2
3
4
5
6
7
8
9
10
11
12
13
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 判断是否有Web环境
this.webEnvironment = deduceWebEnvironment();
// 获取所有ApplicationContextInitializer型的类
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 获取所有ApplicationListener型的类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
  • deduceWebEnvironment()判断是否有Web环境,主要通过判断是否同时存在javax.servlet.Servletorg.springframework.web.context.ConfigurableWebApplicationContext这两个类
  • getSpringFactoriesInstances()是从所有jar中含有META-INF/spring.factories,从该文件里读取配置信息。
  • setInitializers()读取所有ApplicationContextInitializer类型的类,放在List<ApplicationContextInitializer<?>> initializers这个列表里。
  • setListeners()读取所有ApplicationListener类型的类,放在List<ApplicationListener<?>> listeners这个列表里。

getSpringFactoriesInstances()

1
2
3
4
5
6
7
8
9
10
11
12
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 从META-INF/spring.factories获取类型为type的所有类名
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 根据类名,创建实例对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
  • SpringFactoriesLoader.loadFactoryNames从META-INF/spring.factories配置文件,获取类型为type的类名。
  • createSpringFactoriesInstances根据类名,创建实例对象。

loadFactoryNames()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
// 获取所有jar包中的META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
// 从META-INF/spring.factories载入配置
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
// 根据factoryClassName从配置中获取所有factoryClassNames(可能有多个)
String factoryClassNames = properties.getProperty(factoryClassName);
// 多个factoryClassName,用逗号分隔,转成数组
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

  获取所有jar包的META-INF/spring.factories文件,这个文件是一个properties型的配置文件。
  根据factoryClassNames从配置文件里获取factoryClassNames,有可能是多个类名。所以调用StringUtils.commaDelimitedListToStringArray转成数组

Spring-Boot-1.5.4.RELEASE.jar里的META-INF/spring.factories文件信息
spring.factories

run()

  上面的initialize()方法只是载入配置信息,没有执行任何Spring相关的调用,而run()才是真正的通过配置运行程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
// 从META-INF/spring.factories载入SpringApplicationRunListener型的类
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 从运行环境获取参数
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 打印Spring Boot的logo
Banner printedBanner = printBanner(environment);
// 根据是否为Web环境,创建上下文
context = createApplicationContext();
// 从META-INF/spring.factories载入FailureAnalyzer型的类
analyzers = new FailureAnalyzers(context);
// 触发所有Initializer和Linstener
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}

  • SpringApplicationRunListeners listeners = getRunListeners(args);这段也是从META-INF/spring.factories载入SpringApplicationRunListener型的类。
  • ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);这段是从运行环境获取参数,例如:activeProfiles,SystemEnvironment,SystemProperties等。
  • createApplicationContext(),根据是否为web环境,创建不同的ApplicationContext。
  • analyzers = new FailureAnalyzers(context)从META-INF/spring.factories载入FailureAnalyzer型的类,抛异常时的处理
  • prepareContext,触发所有Initializer和Linstener(这两种类型的对象来自于spring.factories)

printBanner()

打印Spring Boot的logo

1
2
3
4
5
6
7
8
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.4.RELEASE)

prepareContext()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 触发所有ApplicationContextInitializer类的initialize()方法
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
// 触发所有SpringApplicationRunListeners的contextLoaded事件
listeners.contextLoaded(context);
}

prepareContext主要有两大处理

  • applyInitializers触发所有ApplicationContextInitializer类的initialize()方法
  • listeners.contextLoaded(context)// 触发所有SpringApplicationRunListeners的contextLoaded事件

refresh()

载入Spring Bean,相当于Spring容器启动做的一些处理的,比如prepareBeanFactory(),postProcessBeanFactory(),registerBeanPostProcessors等。
具体的可以了解Spring的启动流程。

参考资料

  • Spring Boot 1.5.4.RELEASE源码