`
shinesuo
  • 浏览: 156122 次
  • 性别: Icon_minigender_1
  • 来自: 宇宙
社区版块
存档分类
最新评论

Spring Boot 源码分析 —— ApplicationContextInitializer

 
阅读更多

1. 概述

本文,我们来补充 《精尽 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}.
*
* <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);

}
  • 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<T> type) 方法,获得 ApplicationContextInitializer 集合。代码如下:

// SpringApplication.java

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// <1> 加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// <2> 创建对象们
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// <3> 排序对象们
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
  • <1> 处,加载指定 ApplicationContextInitializer 类型对应的,在 META-INF/spring.factories 里的类名的数组。
    • 假设只在 Spring MVC 的环境下,initializers 属性的结果如下图:`initializers` 属性
    • 艿艿整理了 Spring Boot 中,ApplicationContextInitializer 的实现类们,如下图所示:Spring Boot ApplicationContextInitializer 的实现类
  • <2> 处,创建对象们。
  • <3> 处,排序对象们。这个就是在 「2. 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<Class<?>> initializerClasses = getInitializerClasses(environment);
// 如果非空,则进行初始化
if (!initializerClasses.isEmpty()) {
applyInitializerClasses(context, initializerClasses);
}
}
  • <1> 处,调用 #getInitializerClasses(ConfigurableEnvironment env) 方法,获得环境变量配置的 ApplicationContextInitializer 集合们。代码如下:
    // DelegatingApplicationContextInitializer.java

    private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
    // 获得环境变量配置的属性
    String classNames = env.getProperty(PROPERTY_NAME);
    // 拼装成数组,按照 ,分隔
    List<Class<?>> 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<Class<?>> initializerClasses) 方法,执行初始化。代码如下:

    // DelegatingApplicationContextInitializer.java

    private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) {
    Class<?> contextClass = context.getClass();
    // 遍历 initializerClasses 数组,创建对应的 ApplicationContextInitializer 对象们 ①
    List<ApplicationContextInitializer<?>> 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<ApplicationContextInitializer<?>> 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 内部类

相关推荐

    spring boot整合JPA——demo

    本示例“spring boot整合JPA——demo”将演示如何在Spring Boot项目中配置和使用JPA。 首先,我们需要理解Spring Boot与JPA的关系。Spring Boot是基于Spring框架的快速开发工具,它通过自动化配置减少了常规设置...

    Spring Boot课件1 —— 创建和运行Spring Boot项目

    **Spring Boot创建与运行项目详解** Spring Boot是Java开发领域中的一个热门框架,它通过简化配置和自动装配,使得创建和运行Spring应用变得更加容易。在本篇内容中,我们将深入探讨如何利用Spring Boot来创建和...

    spring boot源码分析

    深入学习spring boot 懂得各个标签,注解的用途和原理

    果子学院Spring boot源码解析

    《果子学院Spring Boot源码解析》是一套深入学习Spring Boot源码的教程,旨在帮助开发者深入了解这个流行的Java开发框架的内部工作机制。Spring Boot简化了Java应用的初始搭建以及开发过程,它集成了大量常用的第三...

    第三节-springboot源码解析-王炸篇.pdf

    通过源码分析,开发者可以更好地理解Spring Boot的自动装配、启动流程以及如何自定义启动器。Spring Boot的自动装配原理涉及到Spring Boot的核心特性,它简化了基于Spring的应用开发,通过自动配置减少了大量的配置...

    Spring Boot源码(spring-boot-2.6.2.zip)

    在这个版本中,我们将深入探讨Spring Boot的核心特性、工作原理以及如何通过源码来理解其内部机制。 首先,Spring Boot的核心理念是“约定优于配置”,它通过预设许多默认配置,减少了开发者需要手动配置的繁琐工作...

    Spring Boot实战派(源码)

    《Spring Boot实战派》源码提供了丰富的学习材料,旨在帮助开发者深入理解并...通过分析《Spring Boot实战派》源码,读者不仅可以了解上述技术点,还能学习到如何在实际项目中应用这些技术,提升开发效率和代码质量。

    基于spring boot餐厅管理系统源码.zip

    基于spring boot餐厅管理系统源码 基于spring boot餐厅管理系统源码 基于spring boot餐厅管理系统源码 基于spring boot餐厅管理系统源码 基于spring boot餐厅管理系统源码 基于spring boot餐厅管理系统源码 ...

    spring-boot源码

    下面,我们将深入探讨Spring Boot的源码,揭示其内部工作原理。 1. **自动配置**:Spring Boot的自动配置是其核心特性之一。在`spring-boot-autoconfigure`模块中,通过条件注解(如`@ConditionalOnClass`, `@...

    java毕业设计——基于spring boot的音乐播放网站设计与实现(源码+数据库).zip

    java毕业设计——基于spring boot的音乐播放网站设计与实现(源码+数据库).zip java毕业设计——基于spring boot的音乐播放网站设计与实现(源码+数据库).zip java毕业设计——基于spring boot的音乐播放网站设计与...

    基于 Spring Boot + MySQL 开发的博客系统源码.zip

    基于 Spring Boot + MySQL 开发的博客系统源码 基于 Spring Boot + MySQL 开发的博客系统源码 基于 Spring Boot + MySQL 开发的博客系统源码 基于 Spring Boot + MySQL 开发的博客系统源码 基于 Spring ...

    Spring Boot源码(spring-boot-2.6.2.tar.gz)

    Spring Boot是Java开发领域的一款非常流行的框架,它简化了基于Spring的应用程序开发流程。Spring Boot 2.6.2是该框架...通过分析源码,我们可以学习到Spring框架的最佳实践,以及如何设计和实现一个健壮的微服务架构。

    spring-boot-2.7.0.zip源码

    通过深入阅读和分析Spring Boot 2.7.0的源码,我们可以了解到Spring Boot是如何实现其核心特性的,以及如何利用Spring Framework进行扩展和定制。同时,这也有助于我们更好地利用Spring Boot进行微服务开发,提高...

    《Vue Spring Boot前后端分离开发实战》源码Vue+Spring Boot前后端分离开发实战教学课件(PPT)

    这本《Vue Spring Boot前后端分离开发实战》的源码提供了深入学习和实践这一技术栈的机会。以下是对其中涉及知识点的详细说明: 1. **Vue.js**:Vue.js是一个轻量级的前端JavaScript框架,以其易学易用、组件化和...

    Spring5 源码分析(第 2 版) .zip

    《Spring5 源码分析(第 2 版)》是针对Spring框架第五个主要版本的深度解析著作,由知名讲师倾心打造,旨在帮助读者深入理解Spring框架的内部工作机制,提升对Java企业级应用开发的专业技能。本书涵盖了Spring框架的...

    spring boot 深入浅出源码

    在深入理解Spring Boot的源码时,我们首先要明白其核心设计理念——“约定优于配置”。Spring Boot通过预设默认配置,使得开发者能够快速启动并运行应用,同时提供了丰富的启动器(Starters)来简化依赖管理。 ...

    使用Gradle 构建spring Boot工程系列项目源码(配合第五篇文章)

    本资源包"使用Gradle构建Spring Boot工程系列项目源码"是针对一系列教程的配套源代码,旨在帮助开发者深入理解如何利用Gradle有效地构建Spring Boot应用程序。通过分析这些源码,我们可以学习到以下关键知识点: 1....

    java maven工程 spring boot 学习源码

    本学习资源包“java maven工程 spring boot 学习源码”提供了一个可以直接运行的示例工程,有助于深入理解Spring Boot和Maven的结合使用。 首先,我们需要了解Spring Boot的核心特性。Spring Boot通过内嵌的Servlet...

    java毕业设计——基于spring boot的就业信息管理网站设计与实现(源码+数据库).zip

    java毕业设计——基于spring boot的就业信息管理网站设计与实现(源码+数据库).zip java毕业设计——基于spring boot的就业信息管理网站设计与实现(源码+数据库).zip java毕业设计——基于spring boot的就业信息管理...

    spring boot admin demo 源码 java 服务器 监控

    5. **源码分析**:在提供的压缩包 `springcloud-test` 中,可能包含了 Spring Boot Admin 与 Spring Boot 应用集成的示例代码。你可以通过查看 `Application.java` 文件来了解如何启动和配置服务器及客户端,同时...

Global site tag (gtag.js) - Google Analytics