Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[pull] master from ipipman:master #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
pull merged 1 commit into Mu-L:master from ipipman:master
Aug 2, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions springboot-source-code-analysis/(4)系统初始化器解析.md
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
### 系统初始化器解析

#### 1、ApplicationContextInitializer作用

```java
/**
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
*
* <p>Typically used within web applications that require some programmatic initialization
* of the application context. For example, registering property sources or activating
* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
* for declaring a "contextInitializerClasses" context-param and init-param, respectively.
*
* <p>{@code ApplicationContextInitializer} processors are encouraged to detect
* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
* implemented or if the @{@link org.springframework.core.annotation.Order Order}
* annotation is present and to sort instances accordingly if so prior to invocation.
*
* @author Chris Beams
* @since 3.1
* @param <C> the application context type
* @see org.springframework.web.context.ContextLoader#customizeContext
* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
* @see org.springframework.web.servlet.FrameworkServlet#applyInitializers
*/
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}

```

- 上下文刷新即refersh方法前调用;
- 用来编码设置一些属性变量,通常用在web环境中;
- 可以通过order接口进行排序;



#### 2、ApplicationContextInitializer调用点,通过SpringFactoriesLoder中的this.initializers进行初始化

<img src="https://tva1.sinaimg.cn/large/008i3skNly1gt2l2yqsddj30zg0u077q.jpg" alt="image-20210802174243992" align="left" width="600" />

- 步骤一:在刷新上下文前,调用prepareContext#()准备上下文的方法

```java
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
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);
}
listeners.started(context);
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;
}
```

- 步骤二:调用初始化器 applyInitializers(context) 方法

```java
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 调用初始化器方法
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
```

- 步骤三:系统初始化器的具体实现,调用 ApplicationContextInitializer.initialize() 方法

```java
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
// getInitializers() 方法,从this.initializers 成员中,获取初始化器的实现
for (ApplicationContextInitializer initializer : getInitializers()) {
// 判断初始化器的实现类,是否实现了ApplicationContextInitializer接口类
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
// 调用初始化器实现类中的 initialize#() 方法,进行初始化
initializer.initialize(context);
}
}
```



#### 3、添加系统初始化器 this.initializers 成员的两种方式

- 第一种:定义在spring.factories文件中,通过SpringFactoriesLoader发现与注册

```java
SpringBoot框架中从类路径jar包中读取特定的文件(META-INF/spring.factories)实现扩展类的载入
最终调用SpringApplication.addInitializers#()方法添加到this.initializers成员中
```



- 第二种:通过SpringApplication.addInitializers#() 方法手动添加到系统初始化器中

```java
public static void main(String[] args) {
// SpringApplication.run(SpringbootSourceCodeAnalysisApplication.class, args);
SpringApplication springApplication = new SpringApplication(SpringbootSourceCodeAnalysisApplication.class);
// 手动添加一个框架的初始化器
springApplication.addInitializers(new SecondInitializer());
springApplication.run();
}
```



- 第三种:在application.properties 配置文件中定义环境变量,通过DelegatingApplicationContextInitializer 发现与注册

- 步骤一:DelegatingApplicationContextInitializer 在Spring框架中,会被SpringFactoriesLoader 自动加载,且优先级最高 order = 0

```java
# 在spring.factories配置文件中
# Application Context Initializers
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
```

- 步骤二:通过DelegatingApplicationContextInitializer 中的 initialize#() 方法,获取ApplicationContextInitializer.class的实现

```java
private static final String PROPERTY_NAME = "context.initializer.classes";

private int order = 0;

@Override
public void initialize(ConfigurableApplicationContext context) {
// 获取application.properties配置中的 context.initializer.classes 属性
ConfigurableEnvironment environment = context.getEnvironment();
// 获取所有 ApplicationContextInitializer.class 接口的实现类
List<Class<?>> initializerClasses = getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
// 初始化系统初始化器
applyInitializerClasses(context, initializerClasses);
}
}
```



#### 4、ApplicationContextInitializer.class 的调用流程总结

<img src="https://tva1.sinaimg.cn/large/008i3skNly1gt2mlwhgguj315m0u077s.jpg" alt="image-20210802183526156" align="left" width="500" />



AltStyle によって変換されたページ (->オリジナル) /