1.简单示例:

SpringBoot中的的配置简单属性类支持ConfigurationProperties方式,看一个简单的示例。

 1 @ConfigurationProperties(prefix = "org.dragonfei.demo")
 2 public class DemoProperties {
 3     private String name;
 4     private String password;
 5     private String test;
 6
 7     public String getName() {
 8         return name;
 9     }
10
11     public void setName(String name) {
12         this.name = name;
13     }
14
15     public String getPassword() {
16         return password;
17     }
18
19     public void setPassword(String password) {
20         this.password = password;
21     }
22
23     public String getTest() {
24         return test;
25     }
26
27     public void setTest(String test) {
28         this.test = test;
29     }
30
31     @Override
32     public String toString() {
33         return "DemoProperties{" +
34                 "name='" + name + '\'' +
35                 ", password='" + password + '\'' +
36                 ", test='" + test + '\'' +
37                 '}';
38     }
39 }

定义Properties类

1 org.dragonfei.demo.name=dragonfei
2 org.dragonfei.demo.password=password
3 org.dragonfei.demo.test=test

添加配置

1 @Configuration
2 @EnableConfigurationProperties({DemoProperties.class})
3 public class DemoConfiguration {
4 }

注入Properties

 1 @RunWith(SpringJUnit4ClassRunner.class)
 2 @SpringApplicationConfiguration(classes = DemoConfiguration.class)
 3 @EnableAutoConfiguration
 4 public class DemoPropertiesTest {
 5
 6     @Autowired
 7     private DemoProperties properties;
 8     @Test
 9     public void testProperties(){
10         System.out.println(properties.toString());
11     }
12 }

简单单元测试

1 DemoProperties{name='dragonfei', password='password', test='test'}

运行单元测试结果

DemoProperties神奇的注入到Spring容器中了。有没有跟我一样很兴奋,这样的 一大好处,将配置文件的属性以类的形式展现,在需要使用的时候只需要,autowire需要的类就可以了,避免大片重复的的${a.b.c}

 2.Properties属性自动装配实现

DemoProperties这么神奇注入到容器中,天下没有什么是莫名奇妙的,引出了两个关键问题:

  • DemoProperties是怎样注入到容器中?
  • DemoProperties中的各个属性是怎么被赋值的呢?

  要回答上面的问题,必须对@Configuration如何注入bean做一个简单的回顾:

  • 在解析@Congiguraion的时候,会调用@Import中引入的类
  • 如果@Import中是ImportBeanDefinitionegistar的子类,会直接调用registerBeanDefinitions
  • 如果@Import中是ImportSelector类型,会调用selectImports()返回的bean的registerBeanDefinitions方法。
  • registerBeanDefinitions方法会向BeanFactory中添加新的bean。

回到正题。打开EnableConfigurationProperties

 1 @Target(ElementType.TYPE)
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @Documented
 4 @Import(EnableConfigurationPropertiesImportSelector.class)
 5 public @interface EnableConfigurationProperties {
 6
 7     /**
 8      * Convenient way to quickly register {@link ConfigurationProperties} annotated beans
 9      * with Spring. Standard Spring Beans will also be scanned regardless of this value.
10      * @return {@link ConfigurationProperties} annotated beans to register
11      */
12     Class<?>[] value() default {};
13
14 }

View Code

注意@Imoport里面的类

 1     public String[] selectImports(AnnotationMetadata metadata) {
 2         MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
 3                 EnableConfigurationProperties.class.getName(), false);
 4         Object[] type = attributes == null ? null
 5                 : (Object[]) attributes.getFirst("value");
 6         if (type == null || type.length == 0) {
 7             return new String[] {
 8                     ConfigurationPropertiesBindingPostProcessorRegistrar.class
 9                             .getName() };
10         }
11         return new String[] { ConfigurationPropertiesBeanRegistrar.class.getName(),
12                 ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };
13     }

View Code

然后,会调用ConfigurationPropertiesBeanRegistar和ConfigurationPropertiesBindingPostProcessorRegistar的registerBeanDefinitions方法,前者是为了注入配置properties类,后者为属性绑定值

 1     @Override
 2         public void registerBeanDefinitions(AnnotationMetadata metadata,
 3                 BeanDefinitionRegistry registry) {
 4             MultiValueMap<String, Object> attributes = metadata
 5                     .getAllAnnotationAttributes(
 6                             EnableConfigurationProperties.class.getName(), false);
 7             List<Class<?>> types = collectClasses(attributes.get("value"));
 8             for (Class<?> type : types) {
 9                 String prefix = extractPrefix(type);
10                 String name = (StringUtils.hasText(prefix) ? prefix + "-" + type.getName()
11                         : type.getName());
12                 if (!registry.containsBeanDefinition(name)) {
13                     registerBeanDefinition(registry, type, name);
14                 }
15             }
16         }

ConfigurationPropertiesBeanRegistar之registerBeanDefinitions

 1     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
 2             BeanDefinitionRegistry registry) {
 3         if (!registry.containsBeanDefinition(BINDER_BEAN_NAME)) {
 4             BeanDefinitionBuilder meta = BeanDefinitionBuilder
 5                     .genericBeanDefinition(ConfigurationBeanFactoryMetaData.class);
 6             BeanDefinitionBuilder bean = BeanDefinitionBuilder.genericBeanDefinition(
 7                     ConfigurationPropertiesBindingPostProcessor.class);
 8             bean.addPropertyReference("beanMetaDataStore", METADATA_BEAN_NAME);
 9             registry.registerBeanDefinition(BINDER_BEAN_NAME, bean.getBeanDefinition());
10             registry.registerBeanDefinition(METADATA_BEAN_NAME, meta.getBeanDefinition());
11         }
12     }

ConfigurationPropertiesBindingPostProcessorRegistar之registerBeanDefinition

注意这里注入了ConfigurationPropertiesBindingPostProcessor,这才是属性赋值的关键。查看类图

注意到ConfigurationPropertiesBindingPostProcessor继承自BeanPostProcessor,他会在bean初始化前后调用before和after后置处理,这里,在Properties属性初始化完成后,会对绑定属性,

 1 private void postProcessBeforeInitialization(Object bean, String beanName,
 2             ConfigurationProperties annotation) {
 3         Object target = bean;
 4         PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
 5                 target);
 6         if (annotation != null && annotation.locations().length != 0) {
 7             factory.setPropertySources(
 8                     loadPropertySources(annotation.locations(), annotation.merge()));
 9         }
10         else {
11             factory.setPropertySources(this.propertySources);
12         }
13         factory.setValidator(determineValidator(bean));
14         // If no explicit conversion service is provided we add one so that (at least)
15         // comma-separated arrays of convertibles can be bound automatically
16         factory.setConversionService(this.conversionService == null
17                 ? getDefaultConversionService() : this.conversionService);
18         if (annotation != null) {
19             factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
20             factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
21             factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
22             factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
23             if (StringUtils.hasLength(annotation.prefix())) {
24                 factory.setTargetName(annotation.prefix());
25             }
26         }
27         try {
28             factory.bindPropertiesToTarget();
29         }
30         catch (Exception ex) {
31             String targetClass = ClassUtils.getShortName(target.getClass());
32             throw new BeanCreationException(beanName, "Could not bind properties to "
33                     + targetClass + " (" + getAnnotationDetails(annotation) + ")", ex);
34         }
35     }

绑定属性

至于真实的数据绑定,会从propertySources中获取,敬请期待....Spring 的数据绑定,这简单提一下关键的地方:

1 Set<String> names = getNames(relaxedTargetNames);
2         PropertyValues propertyValues = getPropertyValues(names, relaxedTargetNames);

View Code

请注意getNames,是获取prefix+属性构成的key值,prefix_property和prefix.property都会获取到

 1     private Set<String> getNames(Iterable<String> prefixes) {
 2         Set<String> names = new LinkedHashSet<String>();
 3         if (this.target != null) {
 4             PropertyDescriptor[] descriptors = BeanUtils
 5                     .getPropertyDescriptors(this.target.getClass());
 6             for (PropertyDescriptor descriptor : descriptors) {
 7                 String name = descriptor.getName();
 8                 if (!name.equals("class")) {
 9                     RelaxedNames relaxedNames = RelaxedNames.forCamelCase(name);
10                     if (prefixes == null) {
11                         for (String relaxedName : relaxedNames) {
12                             names.add(relaxedName);
13                         }
14                     }
15                     else {
16                         for (String prefix : prefixes) {
17                             for (String relaxedName : relaxedNames) {
18                                 names.add(prefix + "." + relaxedName);
19                                 names.add(prefix + "_" + relaxedName);
20                             }
21                         }
22                     }
23                 }
24             }
25         }
26         return names;
27     }

View Code

getPropertyValues会获取到满足上述条件的propertyValues,最后调用spring框架提供数据绑定策略进行数据绑定。

转载于:https://www.cnblogs.com/dragonfei/p/5906474.html

Spring 配置解析之Properties相关推荐

  1. Spring Boot:(六)默认日志logback配置解析

    Spring Boot:(六)默认日志logback配置解析 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候,是带着下面几个问题来查资料的,你呢 如何引入日志? 日 ...

  2. Spring多资源文件properties的配置

    Spring简化了加载资源文件的配置,可以通过<context:property-placeholder去加载,这个元素的写法如下: <context:property-placehold ...

  3. spring配置中加载properties文件方法

    首先,遇到一个问题,spring配置中加载properties文件配置如下: <context:property-placeholder ignore-unresolvable="tr ...

  4. spring.profiles.active配置解析、

    1.在配置springcloud 时候.不了解spring.profiles.active 有什么作用.看看文档去了只有. > 查看官方文档: 2.1 Environment Repositor ...

  5. SpringBoot默认日志logback配置解析

    SpringBoot默认日志logback配置解析 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候,是带着下面几个问题来查资料的,你呢 如何引入日志? 日志输出格式 ...

  6. Spring bean解析 - refresh

    文章目录 refresh prepareRefresh 属性校验 BeanFactory创建 BeanFactory接口 BeanFactory定制 Bean加载 EntityResolver Bea ...

  7. MyBatis02:CRUD操作和配置解析

    MyBatis02:CRUD操作和配置解析 CRUD select 根据id查询用户 接口方法 public interface UserMapper {//查询所有用户List<User> ...

  8. 【SpringBoot】最新版2019Spring Boot配置解析,源码解析(速成SpringBoot)——学习笔记版【2】

    SpringBoot配置文件 文章目录 SpringBoot配置文件 四.配置文件 1.简介 2.YAML用法 2.1 简介 2.2语法 3.为属性注入值 3.1使用.yml配置文件 3.1编写.ym ...

  9. Spring配置笔记

    ##Spring 的简述 1. XML配置 在Spring1.x版本,使用Spring开发全部都是XML配置的Bean,随着项目的扩大,我们经常需要XML文件分别放在不同的配置文件中,需要频繁的将类和 ...

最新文章

  1. 二维数组练习--矩阵的加法和乘法
  2. doc如何装oracle,在Windows7下安装Oracle.doc
  3. Java jvisualvm简要说明
  4. LightSwitch中的权限
  5. 开发板实现645协议C语言,迅为-imx6ull开发板之C语言实现LED例程
  6. 菜鸟要做架构师——java性能优化之for循环
  7. 蓝桥杯 AGLO-152 算法训练 8-2求完数
  8. 业内对楼市回暖时间几成共识:尚需一年
  9. android shell强制删除文件夹_手机文件夹都是英文,看不懂、又不敢删?教你如何辨别、释放内存...
  10. java jdom2_JDOM 生成和解析XML(二)
  11. sklearn.metrics.accuracy_score/precision_score/recall_score、micro/macro/weighted(准确率、召回率)
  12. maven打包jar包到本地仓库
  13. ArcGIS的运行许可文件ecp如何打开?
  14. 2021-2027全球与中国USB智能电源板市场现状及未来发展趋势
  15. pdcch加扰_一种基站/终端及其PDCCH加扰/解扰的方法和装置_2014104470078_权利要求书_专利查询_专利网_钻瓜专利网...
  16. 02—测试用例内容包含、测试用例的设计点
  17. android 发qq,腾讯发布QQ轻聊版 for Android V3.4.3版
  18. 网易有道Redis云原生实战
  19. vue 存取、设置、清除cookie
  20. iOS应用下集成携程api(酒店和飞机票)

热门文章

  1. 放肆地使用UIBezierPath和CAShapeLayer画各种图形
  2. 操作系统——MiniDos
  3. Javascript中字符串输出html的动态链接
  4. HK2000 V2版本 GPRS通讯串口说明
  5. php实现贴吧功能,PHPCMS V9万能字段增加单选功能,实现多条房产信息调用1个开发商信息等效果...
  6. RIP协议相关知识总结
  7. KubeMeet 深圳站完整议题出炉
  8. 函数计算搭建小程序Web应用后端服务
  9. 鹏博士和阿里云数据库产品达成战略合作,共赢企业数智化创新市场
  10. 爱奇艺体验Serverless极致扩缩容,资源利用率提升40%