diff --git "a/springboot-source-code-analysis/(4)347円263円273円347円273円237円345円210円235円345円247円213円345円214円226円345円231円250円350円247円243円346円236円220円.md" "b/springboot-source-code-analysis/(4)347円263円273円347円273円237円345円210円235円345円247円213円345円214円226円345円231円250円350円247円243円346円236円220円.md" new file mode 100644 index 0000000..8e82986 --- /dev/null +++ "b/springboot-source-code-analysis/(4)347円263円273円347円273円237円345円210円235円345円247円213円345円214円226円345円231円250円350円247円243円346円236円220円.md" @@ -0,0 +1,210 @@ +### 系统初始化器解析 + +#### 1、ApplicationContextInitializer作用 + +```java +/** + * Callback interface for initializing a Spring {@link ConfigurableApplicationContext} + * prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}. + * + *

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. + * + *

{@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 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 { + + /** + * Initialize the given application context. + * @param applicationContext the application to configure + */ + void initialize(C applicationContext); +} + +``` + +- 上下文刷新即refersh方法前调用; +- 用来编码设置一些属性变量,通常用在web环境中; +- 可以通过order接口进行排序; + + + +#### 2、ApplicationContextInitializer调用点,通过SpringFactoriesLoder中的this.initializers进行初始化 + +image-20210802174243992 + +- 步骤一:在刷新上下文前,调用prepareContext#()准备上下文的方法 + +```java +public ConfigurableApplicationContext run(String... args) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + ConfigurableApplicationContext context = null; + Collection 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 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> initializerClasses = getInitializerClasses(environment); + if (!initializerClasses.isEmpty()) { + // 初始化系统初始化器 + applyInitializerClasses(context, initializerClasses); + } + } + ``` + + + +#### 4、ApplicationContextInitializer.class 的调用流程总结 + +image-20210802183526156 + + +

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