应用未接入配置中心时,一些配置项(如oss存储配置信息、邮件服务配置信息等)需要从其它数据源获取,下面以从数据库获取配置信息为例。
@Value
既然需要通过从数据库中读取配置信息,那么先了解一下@Value的工作原理:
SpringBoot应用启动
org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String...)
=>org.springframework.boot.SpringApplication#run(java.lang.String...)
=>刷新Spring容器
org.springframework.boot.SpringApplication#refreshContext
=>org.springframework.context.support.AbstractApplicationContext#refresh
实例化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
属性填充
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
74protected void populateBean(String beanName, RootBeanDefinition mbd, { 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
14public void inject(Object target, String beanName, 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
处理@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解析,分为以下几步:
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
=>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata
=>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata
收集标注了@Value等注解的字段;
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties
开始注入值;注入过程中,从DefaultListableBeanFactory中遍历所有的embeddedValueResolver,这些embeddedValueResolvers是StringValueResolver,调用其resolveStringValue方法;
其中有一个PropertySourcesPlaceholderConfigurer类构造的StringValueResolver,调用它的resolveStringValue,最终从一个PropertySourcesPropertyResolver的propertySources中遍历所有的propertySource,其中就有Environment的propertySources,匹配到值后返回。
EnvironmentPostProcessor
从上面可以得出结论,在容器refresh之前,从数据库读取信息封装为MapPropertySource塞入Environment#Property即可。
主要实现类:
1 | public class DatabaseEnvironmentPostProcessor implements EnvironmentPostProcessor { |
spring.factories
1 | org.springframework.boot.env.EnvironmentPostProcessor=\ |
spring.factories
Spring Factories是一种类似于Java SPI的机制,在resources/META-INF/spring.factories文件中配置接口的实现类名称(接口名称=实现类),然后在程序中读取该配置文件并实例化,是spring-boot-starter-xxx的实现基础。
为了实现从数据库读取配置信息的需求,显然需要在容器refresh之前完成从数据库读取并添加到环境变量中。
调用链路
从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
发布ApplicationEnvironmentPreparedEvent
org.springframework.boot.SpringApplication#prepareEnvironment
=>org.springframework.boot.context.event.EventPublishingRunListener#environmentPrepared
监听到事件并执行
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;