扩展EnvironmentPostProcessor从数据库加载配置项&spring.factories

应用未接入配置中心时,一些配置项(如oss存储配置信息、邮件服务配置信息等)需要从其它数据源获取,下面以从数据库获取配置信息为例。

@Value

既然需要通过从数据库中读取配置信息,那么先了解一下@Value的工作原理:

  1. SpringBoot应用启动

    org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String...) =>

    org.springframework.boot.SpringApplication#run(java.lang.String...) =>

  2. 刷新Spring容器

    org.springframework.boot.SpringApplication#refreshContext =>

    org.springframework.context.support.AbstractApplicationContext#refresh

  3. 实例化Bean

    org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization =>

    org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons(实例化非懒加载的单例Bean) =>

    org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String) =>

    org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean =>

    org.springframework.beans.factory.support.AbstractBeanFactory#createBean =>

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

  4. 属性填充

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean =>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    if (bw == null) {
    if (mbd.hasPropertyValues()) {
    throw new BeanCreationException(
    mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
    }
    else {
    // Skip property population phase for null instance.
    return;
    }
    }

    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
    //实例化后
    if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
    return;
    }
    }
    }

    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

    //@Bean(autowire = Autowire.BY_NAME)
    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
    MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
    // Add property values based on autowire by name if applicable.
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
    autowireByName(beanName, mbd, bw, newPvs);
    }
    // Add property values based on autowire by type if applicable.
    if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
    autowireByType(beanName, mbd, bw, newPvs);
    }
    pvs = newPvs;
    }

    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

    PropertyDescriptor[] filteredPds = null;
    if (hasInstAwareBpps) {
    if (pvs == null) {
    pvs = mbd.getPropertyValues();
    }
    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
    //依赖注入入口
    //@Autowired @Value:AutowiredAnnotationBeanPostProcessor @Resource:CommonAnnotationBeanPostProcessor
    PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
    if (pvsToUse == null) {
    if (filteredPds == null) {
    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    }
    pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
    if (pvsToUse == null) {
    return;
    }
    }
    pvs = pvsToUse;
    }
    }
    if (needsDepCheck) {
    if (filteredPds == null) {
    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    }
    checkDependencies(beanName, mbd, filteredPds, pvs);
    }

    if (pvs != null) {
    // MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()会覆盖@Autowired
    applyPropertyValues(beanName, mbd, bw, pvs);
    }
    }

    org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties =>

    org.springframework.beans.factory.annotation.InjectionMetadata#inject =>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectedElement> checkedElements = this.checkedElements;
    Collection<InjectedElement> elementsToIterate =
    (checkedElements != null ? checkedElements : this.injectedElements);
    if (!elementsToIterate.isEmpty()) {
    for (InjectedElement element : elementsToIterate) {
    //遍历每个注入点进行依赖注入
    //Autowired:AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement.inject
    //AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement.inject
    //Resource:InjectionMetadata.InjectedElement.inject
    element.inject(target, beanName, pvs);
    }
    }
    }

    org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue =>

    org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency =>

    org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

  5. 处理@Value

    org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#findValue

    org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue =>

    org.springframework.context.support.PropertySourcesPlaceholderConfigurer#processProperties(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, org.springframework.core.env.ConfigurablePropertyResolver) (把所有的配置文件都变成了一个个propertysource对象,同时把environment对象也包装成了一个propertysource对象,并且一个个propertysource对象存储在了MutablePropertySources中。)

大致上来看,@Value解析,分为以下几步:

  1. org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition =>

    org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata =>

    org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata

    收集标注了@Value等注解的字段;

  2. org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties开始注入值;

  3. 注入过程中,从DefaultListableBeanFactory中遍历所有的embeddedValueResolver,这些embeddedValueResolvers是StringValueResolver,调用其resolveStringValue方法;

  4. 其中有一个PropertySourcesPlaceholderConfigurer类构造的StringValueResolver,调用它的resolveStringValue,最终从一个PropertySourcesPropertyResolver的propertySources中遍历所有的propertySource,其中就有Environment的propertySources,匹配到值后返回。

EnvironmentPostProcessor

从上面可以得出结论,在容器refresh之前,从数据库读取信息封装为MapPropertySource塞入Environment#Property即可。

主要实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class DatabaseEnvironmentPostProcessor implements EnvironmentPostProcessor {

@Override
public void postProcessEnvironment(
ConfigurableEnvironment environment, SpringApplication application) {
if (environment.getPropertySources().contains("databasePropertySources")) {
return;
}
// 命令行参数
boolean commandLineArgs = environment.getPropertySources().contains("commandLineArgs");
if (commandLineArgs) {
environment
.getPropertySources()
.addBefore("commandLineArgs", loadConfigurationFromDatabase(environment));
} else {
if (environment.getProperty("spring.datasource.url") != null) {
environment.getPropertySources().addFirst(loadConfigurationFromDatabase(environment));
// 设置激活的Profile
String activeProfile = environment.getProperty("spring.profiles.active", "prd");
environment.addActiveProfile(activeProfile);
}
}
}

private PropertySource loadConfigurationFromDatabase(ConfigurableEnvironment environment) {
String url = environment.getProperty("spring.datasource.url");
String username = environment.getProperty("spring.datasource.username");
String password = environment.getProperty("spring.datasource.password");
String driverClassName = environment.getProperty("spring.datasource.druid.driver-class-name");
Map<String, ?> configs =
new DatabasePropertySourceLoader(url, username, password, driverClassName).load();
PropertySource propertySource =
new MapPropertySource("databasePropertySources", (Map<String, Object>) configs);
return propertySource;
}
}

spring.factories

1
2
org.springframework.boot.env.EnvironmentPostProcessor=\
cn.imokkkk.env.DatabaseEnvironmentPostProcessor

spring.factories

Spring Factories是一种类似于Java SPI的机制,在resources/META-INF/spring.factories文件中配置接口的实现类名称(接口名称=实现类),然后在程序中读取该配置文件并实例化,是spring-boot-starter-xxx的实现基础。

为了实现从数据库读取配置信息的需求,显然需要在容器refresh之前完成从数据库读取并添加到环境变量中。

调用链路

  1. 从spring.factories加载ApplicationListener对应的监听器,并启动

    org.springframework.boot.SpringApplication#run(java.lang.String...) =>

    org.springframework.boot.SpringApplication#getRunListenersorg.springframework.boot.SpringApplication#run(java.lang.String...) => org.springframework.boot.SpringApplication#getSpringFactoriesInstances(java.lang.Class<T>, java.lang.Class<?>[], java.lang.Object...) => org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames

  2. 发布ApplicationEnvironmentPreparedEvent

    org.springframework.boot.SpringApplication#prepareEnvironment =>

    org.springframework.boot.context.event.EventPublishingRunListener#environmentPrepared

  3. 监听到事件并执行

    org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener =>

    org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEvent =>

    org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEnvironmentPreparedEvent =>

    cn.imokkkk.env.DatabaseEnvironmentPostProcessor#postProcessEnvironment

sql语句:

1
2
3
4
5
6
7
8
>CREATE TABLE `app_config`  (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`config_key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`config_value` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`remark` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`is_halt` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
>) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci;
请作者喝瓶肥宅快乐水