@zhuanxu
2018-02-28T11:25:30.000000Z
字数 7988
阅读 2972
spring-boot

spring-boot的配置包括:
外化配置通过将可变量分离出来,使得我们可以在不同的环境中使用同一份代码。
外化配置表现形式不单单是 .properties 和 .yml 属性文件,还可以使用环境变量和命令行参数等来实现。
由于有好多外部配置来源,所以需要解决配置冲突问题,解决方案是通过优先级,下面是属性读取顺序:

讲了配置后,下一步就是介绍使用方法了:
@Value注解@ConfigurationProperties绑定到配置类上Environment获取 先看第一个@Value注解

第二个@ConfigurationProperties注解
这个东西是个啥呢?看代码:
顺着代码注释我们去看ConfigurationPropertiesBindingPostProcessor和EnableConfigurationProperties。
我们先来看ConfigurationPropertiesBindingPostProcessor:
上面继承图中 ApplicationContextAware 是 Spring 提供的获取 Spring 上下文中指定对象的方法,会在 BeanPostProcessor 之前调用,我们先来看 BeanPostProcessor。
BeanPostProcessor 接口定义了两个方法:
所以我们只需要实现BeanPostProcessor接口,就能够控制Bean初始化前后的操作。
下面我们来探究下到底是怎么实现的,查看实现的原理的万能方式是打断点调试,来看下我们调用栈:
我们先来看第一个方法:AbstractAutowireCapableBeanFactory.initializeBean,
initializeBeaninvokeAwareMethods:负责设置 Spring 上下文中指定对象applyBeanPostProcessorsBeforeInitializationinvokeInitMethods:bean年初始化applyBeanPostProcessorsAfterInitialization
上面我们列举出了主要的方法,看名字大概就能知道功能,来看applyBeanPostProcessorsAfterInitialization。
看里面的重点函数getBeanPostProcessors。
/** BeanPostProcessors to apply in createBean */private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();public List<BeanPostProcessor> getBeanPostProcessors() {return this.beanPostProcessors;}
接着我们看 BeanPostProcessor 对象都添加到集合中去的时机,我们会发现中文件中有个addBeanPostProcessor方法,还是同样方法,打断点看调用链,会发现有好多个地方都调用了
addBeanPostProcessor方法,我们一步一步调试,会发现registerBeanPostProcessors方法:

refreshregisterBeanPostProcessorsPostProcessorRegistrationDelegate.registerBeanPostProcessorsbeanFactory.getBeanNamesForType...
上面一步一步最后会调用到getBeanNamesForType来获取BeanPostProcessor类型的Bean,然后都通过addBeanPostProcessor方法添加。
到此我们分析完了BeanPostProcessor,ConfigurationPropertiesBindingPostProcessor还剩下InitializingBean,看上图
InitializingBean 中 afterPropertiesSet 方法处打断点。
可以说是看到了老朋友,也是AbstractAutowireCapableBeanFactory.initializeBean方法,里面的initializeBean方法如下:
invokeInitMethodsif bean instanceof InitializingBean:bean.afterPropertiesSet()
好了到目前,我们看完了ConfigurationPropertiesBindingPostProcessor的主要实现接口,根据之前的分析能得到接口方法的调用顺序为:
postProcessBeforeInitialization -> afterPropertiesSet -> postProcessAfterInitialization
ConfigurationPropertiesBindingPostProcessor中没有实现postProcessAfterInitialization,因次我们先看第一个方法。
@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {// 获取 ConfigurationProperties 注释ConfigurationProperties annotation = getAnnotation(bean, beanName,ConfigurationProperties.class);if (annotation != null) {// 进行数据绑定bind(bean, beanName, annotation);}return bean;}
我们重点看属性值注入bind方法。
private void bind(Object bean, String beanName, ConfigurationProperties annotation) {ResolvableType type = getBeanType(bean, beanName);Annotation[] annotations = new Annotation[] { annotation }Bindable<?> target = Bindable.of(type).withExistingValue(bean).withAnnotations(annotations);this.configurationPropertiesBinder.bind(target);}
其中 ConfigurationPropertiesBinder 负责数据的绑定,阅读过程中发现Springboot2.0中对绑定进行了重写,具体是包:org.springframework.boot.context.properties.bind,下次分析的。
还记得之前的图:
介绍完 ConfigurationPropertiesBindingPostProcessor 后,我们介绍 EnableConfigurationProperties。
@Import(EnableConfigurationPropertiesImportSelector.class)public @interface EnableConfigurationProperties {Class<?>[] value() default {};}
@Import 可以将对应的 Bean 导入到 Spring 上下文中。如果类在工程中的话那么直接使用 @Configuration 注解即可,Spring 会自动识别的。但是如果在其他 jar 包或框架上,没有配置到自动扫描的目录中或者是没有添加 @Configuration 注解,那么就需要使用 @Import 将其导入到项目中来。
public @interface Import {/*** 可以是 Configuration, ImportSelector, ImportBeanDefinitionRegistrar*/Class<?>[] value();}
此处 EnableConfigurationPropertiesImportSelector 实现了 ImportSelector 的 selectImports 方法:
public interface ImportSelector {/*** Select and return the names of which class(es) should be imported based on* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.*/String[] selectImports(AnnotationMetadata importingClassMetadata);}
其中 selectImports 根据注解信息来返回要import的类名,所以我们来看EnableConfigurationPropertiesImportSelector,
里面用到了我们第二个有用的类ImportBeanDefinitionRegistrar:
public interface ImportBeanDefinitionRegistrar {// 注册Beanpublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);}
到这里我们来捋下整个思路:
以上就是我们目前的一个解决问题的思路,下面我们来看 EnableConfigurationProperties 这个Enable**是在哪个地方引入到SpringBoot中的呢?
我们会发现在包org.springframework.boot.autoconfigure下的context.ConfigurationPropertiesAutoConfiguration:
@Configuration@EnableConfigurationPropertiespublic class ConfigurationPropertiesAutoConfiguration {}
至于 ConfigurationPropertiesAutoConfiguration 这个加载进来呢?
这就到了 Spring-boot的精髓 EnableAutoConfiguration 。
我们可以在包org.springframework.boot.autoconfigure中的spring.factories中定义了好多自动配置的类:
所以我们就到了EnableAutoConfiguration:
接着我们就会到了@SpringBootApplication,里面有注解 EnableAutoConfiguration
我们下一步自然就进入@Import(AutoConfigurationImportSelector.class),里面的 selectImports 将 Bean 加载进来:
其中 DeferredImportSelector 的作用是在所有Bean处理完后最后再处理,特别适合Bean是根据条件Import进来的。
挨行来看代码,
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
读取了 META-INF/spring-autoconfigure-metadata.properties 配置文件,改配置文件中的内容如下:
配置文件中配置了 SpringBoot 自动集成的各种 Enable 框架的执行条件,比如定义与其他 AutoConfiguration 框架的执行顺序,需要哪些 Bean 在的时候才可以执行等。
AnnotationAttributes attributes = getAttributes(annotationMetadata);
上面是读取 @EnableAutoConfiguration 注解中配置的 exclude,excludeName 两个属性值。
List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
此处读取 META-INF/spring.factories 配置文件中对应 key:org.springframework.boot.autoconfigure.EnableAutoConfiguration 所对应的类,具体是通过:
其中SpringFactoriesLoader.loadFactoryNames负责将 META-INF/spring.factories 中的key为EnableAutoConfiguration的值读入。
整个流程如下:
图片来自 第05课:@EnableAutoConfiguration 原理与实战
到这我们将介绍我们最后一部分,我们了解了 EnableConfigurationProperties 是如何开启的了,下面就看其加载进入了哪些Bean进来。
我们先不看代码,先讲下这个逻辑,我们有@SpringBootApplication,其开启了@EnableAutoConfiguration,而 @EnableAutoConfiguration 会通过@Import去加载进 META-INF/spring-autoconfigure-metadata.properties 中定义的 autoconfigure 类来, 而其中就包含了 EnableConfigurationProperties,下面我们就看看 EnableConfigurationProperties 的 @Import 去将有相关 的Bean加载进来了,整个逻辑清晰后,我们就能来看 EnableConfigurationPropertiesImportSelector 了,具体方法还是打断点,里面会有两个方法来注册Bean。
会将 EnableConfigurationProperties 中写的Bean注册进来,一个例子就是:
上面会将JacksonProperties加载进来。
这里就将我们之前介绍的ConfigurationPropertiesBindingPostProcessor加载了进来。
以上就是我们的整个spring-boot的配置原理解析,总结下整个思路:
你的鼓励是我继续写下去的动力,期待我们共同进步。
