# 精尽 Spring Boot 源码分析 —— ApplicationContextInitializer # 1. 概述 本文,我们来补充 [《精尽 Spring Boot 源码分析 —— SpringApplication》](http://svip.iocoder.cn/Spring-Boot/SpringApplication) 文章,并未详细解析的 ApplicationContextInitializer 。 # 2. ApplicationContextInitializer `org.springframework.context.ApplicationContextInitializer` ,ApplicationContext 初始化接口。代码如下: ``` /** * 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); } ``` - 1、ApplicationContextInitializer 是 Spring Framework 3.1 版本开始提供的接口。而本文,我们是来分享 Spring Boot 中,几个 ApplicationContextInitializer 实现类。 - 2、【作用】ApplicationContextInitializer 是一个回调接口,用于 Spring ConfigurableApplicationContext 容器执行 `#refresh()` 方法进行初始化之前,提前走一些自定义的初始化逻辑。 - 3、【场景】它的使用场景,例如说 Web 应用中需要注册属性,或者激活 Profiles 。 - 4、【排序】它支持 Spring 的 Ordered 接口、`@Order` 注解,来对多个 ApplicationContextInitializer 实例进行排序,从而实现,ApplicationContextInitializer 按照顺序调用 `#initialize(C applicationContext)` 方法,进行初始化。 # 3. SpringApplication 中的使用 ## 3.1 初始化 ApplicationContextInitializer 集合 在 SpringApplication 构造方法中,会调用 `#getSpringFactoriesInstances(Class type)` 方法,获得 ApplicationContextInitializer 集合。代码如下: ``` // SpringApplication.java private Collection getSpringFactoriesInstances(Class type) { return getSpringFactoriesInstances(type, new Class[] {}); } private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates // <1> 加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组 Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // <2> 创建对象们 List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // <3> 排序对象们 AnnotationAwareOrderComparator.sort(instances); return instances; } ``` - ``` <1> ``` 处,加载指定 ApplicationContextInitializer 类型对应的,在 ``` META-INF/spring.factories ``` 里的类名的数组。 - 假设只在 Spring MVC 的环境下,`initializers` 属性的结果如下图:[![`initializers` 属性](12-Spring Boot 源码分析-ApplicationContextInitializer.assets/01.jpg)](http://static.iocoder.cn/images/Spring-Boot/2021-01-07/01.jpg)`initializers` 属性 - 艿艿整理了 Spring Boot 中,ApplicationContextInitializer 的实现类们,如下图所示:[![Spring Boot ApplicationContextInitializer 的实现类](12-Spring Boot 源码分析-ApplicationContextInitializer.assets/01-17199767902571.jpg)](http://static.iocoder.cn/images/Spring-Boot/2021-01-22/01.jpg)Spring Boot ApplicationContextInitializer 的实现类 - `<2>` 处,创建对象们。 - `<3>` 处,排序对象们。这个就是在 [「2. ApplicationContextInitializer」](https://svip.iocoder.cn/Spring-Boot/ApplicationContextInitializer/#) 提到的【排序】。 ## 3.2 prepareContext 在 `#prepareContext(...)` 方法中,即在 Spring IoC 容器初始化之前,会调用 `#applyInitializers()` 方法,逐个调用 ApplicationContextInitializer 的初始化方法。代码如下: ``` // SpringApplication.java protected void applyInitializers(ConfigurableApplicationContext context) { // 遍历 ApplicationContextInitializer 数组 for (ApplicationContextInitializer initializer : getInitializers()) { // 校验 ApplicationContextInitializer 的泛型非空 Class requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); // 初始化 ApplicationContextInitializer initializer.initialize(context); } } ``` - 比较简单,就是调用 `ApplicationContextInitializer#initialize(context)` 方法,进行初始化。 下面,我们来逐个看看 Spring Boot 对 ApplicationContextInitializer 的实现类们。 # 4. DelegatingApplicationContextInitializer `org.springframework.boot.context.config.DelegatingApplicationContextInitializer` ,实现 ApplicationContextInitializer、Ordered 接口,根据环境变量配置的 `context.initializer.classes` 配置的 ApplicationContextInitializer 类们,交给它们进行初始化。 ## 4.1 构造方法 ``` // DelegatingApplicationContextInitializer.java /** * 环境变量配置的属性 */ private static final String PROPERTY_NAME = "context.initializer.classes"; /** * 默认优先级 */ private int order = 0; // ... 构造方法为空 @Override public int getOrder() { return this.order; } ``` - 优先级为 0 ,在 Spring Boot 默认的 ApplicationContextInitializer 实现类中,是排在最前面的。 ## 4.2 initialize 实现 `#initialize(ConfigurableApplicationContext context)` 方法,代码如下: ``` // DelegatingApplicationContextInitializer.java @Override public void initialize(ConfigurableApplicationContext context) { // <1> 获得环境变量配置的 ApplicationContextInitializer 集合们 ConfigurableEnvironment environment = context.getEnvironment(); List> initializerClasses = getInitializerClasses(environment); // 如果非空,则进行初始化 if (!initializerClasses.isEmpty()) { applyInitializerClasses(context, initializerClasses); } } ``` - ``` <1> ``` 处,调用 ``` #getInitializerClasses(ConfigurableEnvironment env) ``` 方法,获得环境变量配置的 ApplicationContextInitializer 集合们。代码如下: ``` // DelegatingApplicationContextInitializer.java private List> getInitializerClasses(ConfigurableEnvironment env) { // 获得环境变量配置的属性 String classNames = env.getProperty(PROPERTY_NAME); // 拼装成数组,按照 ,分隔 List> classes = new ArrayList<>(); if (StringUtils.hasLength(classNames)) { for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) { classes.add(getInitializerClass(className)); } } return classes; } private Class getInitializerClass(String className) throws LinkageError { try { // 获得全类名,对应的类 Class initializerClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader()); Assert.isAssignable(ApplicationContextInitializer.class, initializerClass); return initializerClass; } catch (ClassNotFoundException ex) { throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex); } } ``` - `<2>` 处,调用 `#applyInitializerClasses(ConfigurableApplicationContext context, List> initializerClasses)` 方法,执行初始化。代码如下: ``` // DelegatingApplicationContextInitializer.java private void applyInitializerClasses(ConfigurableApplicationContext context, List> initializerClasses) { Class contextClass = context.getClass(); // 遍历 initializerClasses 数组,创建对应的 ApplicationContextInitializer 对象们 ① List> initializers = new ArrayList<>(); for (Class initializerClass : initializerClasses) { initializers.add(instantiateInitializer(contextClass, initializerClass)); } // 执行 ApplicationContextInitializer 们的初始化逻辑 ② applyInitializers(context, initializers); } // 被 ① 处调用 private ApplicationContextInitializer instantiateInitializer(Class contextClass, Class initializerClass) { // 断言校验 Class requireContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); Assert.isAssignable(requireContextClass, contextClass, String.format( "Could not add context initializer [%s]" + " as its generic parameter [%s] is not assignable " + "from the type of application context used by this " + "context loader [%s]: ", initializerClass.getName(), requireContextClass.getName(), contextClass.getName())); // 创建 ApplicationContextInitializer 对象 return (ApplicationContextInitializer) BeanUtils.instantiateClass(initializerClass); } // 被 ② 处调用 @SuppressWarnings({ "unchecked", "rawtypes" }) private void applyInitializers(ConfigurableApplicationContext context, List> initializers) { // 排序,无处不在的排序! initializers.sort(new AnnotationAwareOrderComparator()); // 执行初始化逻辑 for (ApplicationContextInitializer initializer : initializers) { initializer.initialize(context); } } ``` - 虽然代码有点长,但是简单的。 # 5. SharedMetadataReaderFactoryContextInitializer `org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer` ,实现 ApplicationContextInitializer、Ordered 接口,它会创建一个用于在 ConfigurationClassPostProcessor 和 Spring Boot 间共享的 CachingMetadataReaderFactory Bean 对象。 简化代码如下: ``` /** * {@link ApplicationContextInitializer} to create a shared * {@link CachingMetadataReaderFactory} between the * {@link ConfigurationClassPostProcessor} and Spring Boot. * * @author Phillip Webb * @since 1.4.0 */ /** * 创建的 CachingMetadataReaderFactory 的 Bean 名字 */ public static final String BEAN_NAME = "org.springframework.boot.autoconfigure." + "internalCachingMetadataReaderFactory"; @Override public void initialize(ConfigurableApplicationContext applicationContext) { applicationContext.addBeanFactoryPostProcessor(new CachingMetadataReaderFactoryPostProcessor()); } @Override public int getOrder() { return 0; } // ... 省略 CachingMetadataReaderFactoryPostProcessor 内部类 ``` - 看不懂这个类的具体用途,暂时先不去深究。😈 等真的需要它时,咱们在细细的撸它。 # 6. ContextIdApplicationContextInitializer `org.springframework.boot.context.ContextIdApplicationContextInitializer` ,实现 ApplicationContextInitializer、Ordered 接口,负责生成 Spring 容器的编号。 ## 6.1 构造方法 ``` // ContextIdApplicationContextInitializer.java /** * 优先级 */ private int order = Ordered.LOWEST_PRECEDENCE - 10; @Override public int getOrder() { return this.order; } ``` ## 6.2 initialize 实现 `#initialize(ConfigurableApplicationContext context)` 方法,代码如下: ``` // ContextIdApplicationContextInitializer.java @Override public void initialize(ConfigurableApplicationContext applicationContext) { // <1> 获得(创建) ContextId 对象 ContextId contextId = getContextId(applicationContext); // <2> 设置到 applicationContext 中 applicationContext.setId(contextId.getId()); // <3> 注册到 contextId 到 Spring 容器中 applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId); } ``` - ContextId ,是 ContextIdApplicationContextInitializer 的内部类,Spring 容器编号的封装。代码如下: ``` // ContextIdApplicationContextInitializer#ContextId.java class ContextId { /** * 递增序列 */ private final AtomicLong children = new AtomicLong(0); /** * 编号 */ private final String id; ContextId(String id) { this.id = id; } /** * @return 创建子 Context 的编号 */ ContextId createChildId() { return new ContextId(this.id + "-" + this.children.incrementAndGet()); } String getId() { return this.id; } } ``` - `<1>` 处,调用 `#getContextId(ConfigurableApplicationContext applicationContext)` 方法,获得(创建) ContextId 对象。代码如下: ``` // ContextIdApplicationContextInitializer.java private ContextId getContextId(ConfigurableApplicationContext applicationContext) { // 获得父 ApplicationContext 对象 ApplicationContext parent = applicationContext.getParent(); // 情况一,如果父 ApplicationContext 存在,且有对应的 ContextId 对象,则使用它生成当前容器的 ContextId 对象 if (parent != null && parent.containsBean(ContextId.class.getName())) { return parent.getBean(ContextId.class).createChildId(); } // 情况二,创建 ContextId 对象 return new ContextId(getApplicationId(applicationContext.getEnvironment())); } private String getApplicationId(ConfigurableEnvironment environment) { String name = environment.getProperty("spring.application.name"); return StringUtils.hasText(name) ? name : "application"; } ``` - 一般情况下,使用 `"spring.application.name"` 环境变量,作为 ContextId 对象的 `id` 属性。 - `<2>` 处,设置到 `applicationContext.id` 中。 - `<3>` 处,注册到 `contextId` 到 Spring 容器中。这样,后续就可以拿到了。 # 7. ConfigurationWarningsApplicationContextInitializer > 艿艿:对于这个类,选择性了解即可。 `org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer` ,实现 ApplicationContextInitializer 接口,用于检查配置,报告错误的配置。如下是其类上的注释: ``` /** * {@link ApplicationContextInitializer} to report warnings for common misconfiguration mistakes. */ ``` ## 7.1 initialize 实现 `#initialize(ConfigurableApplicationContext applicationContext)` 方法,代码如下: ``` // ConfigurationWarningsApplicationContextInitializer.java @Override public void initialize(ConfigurableApplicationContext context) { // 注册 ConfigurationWarningsPostProcessor 到 Spring 容器中 context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks())); } ``` - 注册 ConfigurationWarningsPostProcessor 到 Spring 容器中。关于 ConfigurationWarningsPostProcessor 类,在 [「7.2 ConfigurationWarningsPostProcessor」](https://svip.iocoder.cn/Spring-Boot/ApplicationContextInitializer/#) 中,详细解析。 - 其中,`#getChecks()` 方法,返回 Check 数组。代码如下: ``` // ConfigurationWarningsApplicationContextInitializer.java protected Check[] getChecks() { return new Check[] { new ComponentScanPackageCheck() }; } ``` - 返回的数组,只有一个 ComponentScanPackageCheck 对象。 - Check ,是 ConfigurationWarningsApplicationContextInitializer 的内部接口,校验器。代码如下: ``` // ConfigurationWarningsApplicationContextInitializer.java @FunctionalInterface protected interface Check { /** * Returns a warning if the check fails or {@code null} if there are no problems. * @param registry the {@link BeanDefinitionRegistry} * @return a warning message or {@code null} */ String getWarning(BeanDefinitionRegistry registry); } ``` - 看到此处,胖友可能有点懵逼,不着急。在 [「7.2 ConfigurationWarningsPostProcessor」](https://svip.iocoder.cn/Spring-Boot/ApplicationContextInitializer/#) 中,会串起来滴。 ## 7.2 ConfigurationWarningsPostProcessor ConfigurationWarningsPostProcessor ,是 ConfigurationWarningsApplicationContextInitializer 的内部静态类,实现 PriorityOrdered、BeanDefinitionRegistryPostProcessor 接口,代码如下: ``` // ConfigurationWarningsPostProcessor#ConfigurationWarningsPostProcessor.java protected static final class ConfigurationWarningsPostProcessor implements PriorityOrdered, BeanDefinitionRegistryPostProcessor { /** * Check 数组 */ private Check[] checks; public ConfigurationWarningsPostProcessor(Check[] checks) { this.checks = checks; } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE - 1; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {} @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // 遍历 Check 数组,执行校验。若有错,则打印 warn 日志 for (Check check : this.checks) { String message = check.getWarning(registry); if (StringUtils.hasLength(message)) { warn(message); } } } private void warn(String message) { if (logger.isWarnEnabled()) { logger.warn(String.format("%n%n** WARNING ** : %s%n%n", message)); } } } ``` - 核心就是 `#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)` 方法。在其内部,遍历 Check 数组,执行校验。若有错,则打印 warn 日志。在上文中,我们看到目前 `checks` 只有一个 ComponentScanPackageCheck 元素。关于它,我们在 [「7.3 ComponentScanPackageCheck」](https://svip.iocoder.cn/Spring-Boot/ApplicationContextInitializer/#) 详细解析。 ## 7.3 ComponentScanPackageCheck ComponentScanPackageCheck ,实现 Check 接口,检查是否使用了 `@ComponentScan` 注解,扫描了指定扫描的包。 ### 7.3.1 构造方法 ``` // ConfigurationWarningsPostProcessor#ComponentScanPackageCheck.java /** * 有问题的包的集合。 * * 即禁止使用 @ComponentScan 注解扫描这个集合中的包 */ private static final Set PROBLEM_PACKAGES; static { Set packages = new HashSet<>(); packages.add("org.springframework"); packages.add("org"); PROBLEM_PACKAGES = Collections.unmodifiableSet(packages); } ``` - 即禁止扫描 `"org.springframework"` 和 `"org"` 包。因为 `"org.springframework"` 包下,有非常多的 Bean ,这样扫描,会错误的注入很多 Bean 。 ### 7.3.2 getWarning 实现 `#getWarning(BeanDefinitionRegistry registry)` 方法,代码如下: ``` // ConfigurationWarningsPostProcessor#ComponentScanPackageCheck.java @Override public String getWarning(BeanDefinitionRegistry registry) { // <1> 获得要扫描的包 Set scannedPackages = getComponentScanningPackages(registry); // <2> 获得要扫描的包中,有问题的包 List problematicPackages = getProblematicPackages(scannedPackages); // <3.1> 如果 problematicPackages 为空,说明不存在问题 if (problematicPackages.isEmpty()) { return null; } // <3.2> 如果 problematicPackages 非空,说明有问题,返回错误提示 return "Your ApplicationContext is unlikely to " + "start due to a @ComponentScan of " + StringUtils.collectionToDelimitedString(problematicPackages, ", ") + "."; } ``` - `<1>` 处,调用 `#getComponentScanningPackages(BeanDefinitionRegistry registry)` 方法,获得要扫描的包。代码如下: ``` // ConfigurationWarningsPostProcessor#ComponentScanPackageCheck.java protected Set getComponentScanningPackages(BeanDefinitionRegistry registry) { // 扫描的包的集合 Set packages = new LinkedHashSet<>(); // 获得所有 BeanDefinition 的名字们 String[] names = registry.getBeanDefinitionNames(); for (String name : names) { // 如果是 AnnotatedBeanDefinition BeanDefinition definition = registry.getBeanDefinition(name); if (definition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annotatedDefinition = (AnnotatedBeanDefinition) definition; // 如果有 @ComponentScan 注解,则添加到 packages 中 addComponentScanningPackages(packages, annotatedDefinition.getMetadata()); } } return packages; } private void addComponentScanningPackages(Set packages, AnnotationMetadata metadata) { // 获得 @ComponentScan 注解 AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(ComponentScan.class.getName(), true)); // 如果存在,则添加到 packages 中 if (attributes != null) { addPackages(packages, attributes.getStringArray("value")); addPackages(packages, attributes.getStringArray("basePackages")); addClasses(packages, attributes.getStringArray("basePackageClasses")); if (packages.isEmpty()) { packages.add(ClassUtils.getPackageName(metadata.getClassName())); } } } private void addPackages(Set packages, String[] values) { if (values != null) { Collections.addAll(packages, values); } } private void addClasses(Set packages, String[] values) { if (values != null) { for (String value : values) { packages.add(ClassUtils.getPackageName(value)); } } } ``` - 虽然很长,但是比较简单。就是找 `@ComponentScan` 注解所扫描的包。 - `<2>` 处,调用 `#getProblematicPackages(Set scannedPackages)` 方法,获得要扫描的包中,有问题的包。代码如下: ``` // ConfigurationWarningsPostProcessor#ComponentScanPackageCheck.java private List getProblematicPackages(Set scannedPackages) { // 有问题的包的集合 List problematicPackages = new ArrayList<>(); for (String scannedPackage : scannedPackages) { // 判断是否在 PROBLEM_PACKAGES 中。如果是,则添加到 problematicPackages 中 if (isProblematicPackage(scannedPackage)) { problematicPackages.add(getDisplayName(scannedPackage)); } } return problematicPackages; } private boolean isProblematicPackage(String scannedPackage) { if (scannedPackage == null || scannedPackage.isEmpty()) { return true; } return PROBLEM_PACKAGES.contains(scannedPackage); } private String getDisplayName(String scannedPackage) { if (scannedPackage == null || scannedPackage.isEmpty()) { return "the default package"; } return "'" + scannedPackage + "'"; } ``` - 😈 就是判断 `scannedPackages` 哪些在 `PROBLEM_PACKAGES` 中。 - `<3.1>` 处,如果 `problematicPackages` 为空,说明不存在问题。 - `<3.2>` 处,如果 `problematicPackages` 非空,说明有问题,返回错误提示。 # 8. ServerPortInfoApplicationContextInitializer `org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer` ,实现 ApplicationContextInitializer、ApplicationListener 接口,监听 EmbeddedServletContainerInitializedEvent 类型的事件,然后将内嵌的 Web 服务器使用的端口给设置到 ApplicationContext 中。 ## 8.1 initialize 实现 `#initialize(ConfigurableApplicationContext applicationContext)` 方法,代码如下: ``` // ServerPortInfoApplicationContextInitializer.java @Override public void initialize(ConfigurableApplicationContext applicationContext) { applicationContext.addApplicationListener(this); } ``` - 将自身作为一个 ApplicationListener 监听器,添加到 Spring 容器中。 ## 8.2 onApplicationEvent 实现 `#onApplicationEvent(WebServerInitializedEvent event)` 方法,当监听到 WebServerInitializedEvent 事件,进行触发。代码如下: ``` // ServerPortInfoApplicationContextInitializer.java @Override public void onApplicationEvent(WebServerInitializedEvent event) { // <1> 获得属性名 String propertyName = "local." + getName(event.getApplicationContext()) + ".port"; // <2> 设置端口到 environment 的 propertyName 中 setPortProperty(event.getApplicationContext(), propertyName, event.getWebServer().getPort()); } ``` - `<1>` 处,获得属性名。其中,`#getName(WebServerApplicationContext context)` 方法,获得 WebServer 的名字。代码如下: ``` // ServerPortInfoApplicationContextInitializer.java private String getName(WebServerApplicationContext context) { String name = context.getServerNamespace(); return StringUtils.hasText(name) ? name : "server"; } ``` - `<2>` 处,调用 `#setPortProperty(ApplicationContext context, String propertyName, int port)` 方法,设置端口到 `environment` 的 `propertyName` 中。代码如下: ``` // ServerPortInfoApplicationContextInitializer.java private void setPortProperty(ApplicationContext context, String propertyName, int port) { // 设置端口到 environment 的 propertyName 中 if (context instanceof ConfigurableApplicationContext) { setPortProperty(((ConfigurableApplicationContext) context).getEnvironment(), propertyName, port); } // 如果有父容器,则继续设置 if (context.getParent() != null) { setPortProperty(context.getParent(), propertyName, port); } } @SuppressWarnings("unchecked") private void setPortProperty(ConfigurableEnvironment environment, String propertyName, int port) { MutablePropertySources sources = environment.getPropertySources(); // 获得 "server.ports" 属性对应的值 PropertySource source = sources.get("server.ports"); if (source == null) { source = new MapPropertySource("server.ports", new HashMap<>()); sources.addFirst(source); } // 添加到 source 中 ((Map) source.getSource()).put(propertyName, port); } ``` - 注意噢,设置的属性结果是,`"server.ports"` 中,的 KEY 为 `propertyName` ,VALUE 为 `port` 。😈 # 9. 其他 ApplicationContextInitializer 实现类 ApplicationContextInitializer 还有一些其它实现类,不是很重要,可以选择不看。 - ``` spring-boot-test ``` 模块 - `org.springframework.boot.test.context.ConfigFileApplicationContextInitializer` 类 - `org.springframework.boot.test.context.SpringBootContextLoader` 中的 ParentContextApplicationContextInitializer 类 - ``` spring-boot-devtools ``` 模块 - `org.springframework.boot.devtools.restart.RestartScopeInitializer` 类 - ``` spring-boot-autoconfigure ``` 模块 - `org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener` 类 - ``` spring-boot ``` 模块 - `org.springframework.boot.web.servlet.support.ServletContextApplicationContextInitializer` 类 - `org.springframework.boot.builder.ParentContextApplicationContextInitializer` 类 😈 貌似还是蛮多的。 # 666. 彩蛋 小更一下,算是水文~感觉比较有收获的 ApplicationContextInitializer 的实现类是: - [「6. ContextIdApplicationContextInitializer」](https://svip.iocoder.cn/Spring-Boot/ApplicationContextInitializer/#) - [「8. ServerPortInfoApplicationContextInitializer」](https://svip.iocoder.cn/Spring-Boot/ApplicationContextInitializer/#) 参考和推荐如下文章: - dm_vincent [《[Spring Boot\] 5. Spring Boot 中的ApplicationContext - 执行 ApplicationContextInitializer 初始化器》](https://blog.csdn.net/dm_vincent/article/details/77619780)