forked from ipipman/JavaSpringBootSamples
-
Notifications
You must be signed in to change notification settings - Fork 0
[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
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
210 changes: 210 additions & 0 deletions
springboot-source-code-analysis/(4)系统初始化器解析.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" /> | ||
|
|
||
|
|
||
|
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.