Spring Boot启动过程

Spring Boot启动

Spring Boot项目若从main方法启动,走的是SpringApplication的run方法来启动spring容器,从main的入口开始,一步一步往下看:

1
2
3
4
5
6
7
@SpringBootApplication
public class Application extends SpringBootServletInitializer {

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

接下来就是SpringApplication的run方法了,进入了Spring的流程:

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


public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}

到这一步可以看到,整个启动流程分为两个步骤:初始化一个SpringApplication对象、执行该对象的run方法。

SpringApplication对象初始化

首先看SpringApplication创建的时候是怎么初始化的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public SpringApplication(Object... sources) {
initialize(sources);
}


private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}


//判断是否是web项目
this.webEnvironment = deduceWebEnvironment();


//从spring.factories中获取配置的所有ApplicationContextInitializer初始化器
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));


//从spring.factories中获取配置的所有ApplicationListenerj监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}

这里的信息量还是不少的:

getSpringFactoriesInstances

getSpringFactoriesInstances方法,其作用是找到spring.factories中配置的接口的实现类,这里是用来获取ApplicationContextInitializer和ApplicationListener的实现类

实现方式是利用SpringFactoriesLoader查找META-INF/spring.factories下的配置,按properties取值:

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
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}


/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";


public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
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();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(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);
}
}

ApplicationContextInitializer

ApplicationContextInitializer的作用, 是在应用的上下文ConfigurableApplicationContext做refresh之前,对ConfigurableApplicationContext实例做进一步的设置或处理。而ConfigurableApplicationContext接口继承了ApplicationContext接口。

ApplicationListener

ApplicationListener就是spring中定义的监听器,用来监听后面SpringApplicationRunListener发布的消息,可以在应用启动中监听应用的不同阶段,做不同的操作。

SpringApplication启动

看完了初始化,看一下SpringApplication是怎么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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//加载SpringApplicationRunListener,并发布事件:应用开始启动了
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();

try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);


//创建、配置Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);


//打印spring boot的logo,可定制
Banner printedBanner = printBanner(environment);


//根据是否是web项目,来创建不同的ApplicationContext容器。
context = createApplicationContext();

analyzers = new FailureAnalyzers(context);


//初始化ApplicationContext
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);


//刷新context
refreshContext(context);


//查找、执行注册的CommandLineRunner和ApplicationRunner
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);
}
}

说一说几个关键的阶段

SpringApplicationRunListener

同样是利用SpringFactoriesLoader从spring.factories中查找SpringApplicationRunListener的实现类,只有一个EventPublishingRunListener,本质是一个事件发布者。

事件发布

在应用的不同阶段,发布不同的事件,通知感兴趣的监听器在初始化的过程中做不同的操作。

发布事件的阶段分别在应用开始启动、Environment创建好后、ApplicationContext创建好后、ApplicationContext加载完成且refresh调用前、run方法结束调用前,

事件监听

SpringApplication初始化时加载的ApplicationListener监听各个阶段相关的事件

Environment

Environment主要抽象了配置文件(profile)和属性(properties),当Environment准备好后,在整个应用的任何时候,都可以从Environment中获取资源

ApplicationContext

初始化ApplicationContext

初始化ApplicationContext时的主要工作:

  1. 设置准备好的Environment
  2. 执行所有ApplicationContextInitializer的initialize方法
  3. 发布contextPrepared事件通知监听者
  4. 将所有的bean加载到容器中
  5. 发布contextLoaded事件通知监听者

刷新ApplicationContext

refreshContext方法一路点进去,就会发现最终调的还是AbstractApplicationContext的refresh方法:

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

相信看过spring源码的,对spring初始化了解的都对这段代码很熟…

坚持原创技术分享,您的支持将鼓励我继续创作!
0%