承接前文springboot情操陶冶-@Configuration注解解析,本文将在前文的基础上阐述@ConfigurationProperties注解的使用

@ConfigurationProperties

此注解多用于对配置文件的加载以及映射对应值至相应的java属性中,样例如下


1.配置属性指定application.properties

# user custom
user.custom.username=demo_jing
user.custom.nickname=nanco
user.custom.email=questionsky1211@gmail.com
user.custom.password=demo1234
user.custom.job=programmer

2.属性映射类(使用@ConfigurationProperties注解)UserProperty.java

package com.example.demo.bootbase;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* @author nanco
* @create 2018/8/13
**/
@ConfigurationProperties(prefix = "user.custom")
public class UserProperty { private String username; private String nickname; private String email; private String password; private String job; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getNickname() {
return nickname;
} public void setNickname(String nickname) {
this.nickname = nickname;
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getJob() {
return job;
} public void setJob(String job) {
this.job = job;
} @Override
public String toString() {
return "UserProperty{" +
"username='" + username + '\'' +
", nickname='" + nickname + '\'' +
", email='" + email + '\'' +
", password='" + password + '\'' +
", job='" + job + '\'' +
'}';
}
}

3.属性映射开启(使用@EnableConfigurationProperties注解)UserPropertiesAutoConfiguration.java

package com.example.demo.bootbase;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration; /**
* @author nanco
* @create 2018/8/13
**/
@Configuration
@EnableConfigurationProperties(value = UserProperty.class)
public class UserPropertiesAutoConfiguration {
}

4.上述注解生效入口META-INF\spring.fatories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.demo.bootbase.UserPropertiesAutoConfiguration

5.结果检验

package com.example.demo;

import com.example.demo.bootbase.UserProperty;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext; @SpringBootApplication
public class DemoSpringbootApplication { public static void main(String[] args) {
ApplicationContext demoApplicationContext = SpringApplication.run(DemoSpringbootApplication.class, args); UserProperty userProperty = demoApplicationContext.getBean(UserProperty.class) ; System.out.println(userProperty);
}
}

输出结果如下

...
...
2018-08-13 14:37:05.537 INFO 17384 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147483647
2018-08-13 14:37:05.546 INFO 17384 --- [ main] c.e.demo.DemoSpringbootApplication : Started DemoSpringbootApplication in 2.611 seconds (JVM running for 3.756)
UserProperty{username='demo_jing', nickname='nanco', email='questionsky1211@gmail.com', password='demo1234', job='programmer'}

出现的结果与我们预知的一样,属性都进行了相应的赋予。下面笔者开展下源码层的解析以解开其中的绑定小谜团

@EnableConfigurationProperties

要想上述的结果运行成功,必须在相应的启动类使用@EnableConfigurationProperties注解,我们先看下其源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesImportSelector.class)
public @interface EnableConfigurationProperties { /**
* Convenient way to quickly register {@link ConfigurationProperties} annotated beans
* with Spring. Standard Spring Beans will also be scanned regardless of this value.
* @return {@link ConfigurationProperties} annotated beans to register
*/
Class<?>[] value() default {}; }

其中的value属性便是扫描的属性class,并且会将指定的class属性注册至bean工厂。通过前文分析可得,最终的解析是通过@Import引入的EnableConfigurationPropertiesImportSelector.class来实现的

EnableConfigurationPropertiesImportSelector

直接查看其复写的selectImports()方法

	private static final String[] IMPORTS = {
// 属性类注册
ConfigurationPropertiesBeanRegistrar.class.getName(),
// 属性类绑定处理
ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() }; @Override
public String[] selectImports(AnnotationMetadata metadata) {
return IMPORTS;
}

将导入两个类去处理属性的关系,我们按照顺序来分析


ConfigurationPropertiesBeanRegistrar

直接去看其复写的方法

		@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 读取被注解类上的EnableConfigurationProperties上的value属性并进行注入至bean工厂
getTypes(metadata).forEach((type) -> register(registry,
(ConfigurableListableBeanFactory) registry, type));
}

具体的代码就不贴出来了,笔者此处作下小的总结

  1. 优先读取被注解类上@EnableConfigurationPropertiesvalue属性

  2. 在将上述的value属性指定的class集合注入至bean工厂前,优先判断是否被@ConfigurationProperties注解修饰过,没有则会报错

  3. 将指定的class集合注册至bean工厂


ConfigurationPropertiesBindingPostProcessorRegistrar

直接查看复写的方法

	@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(
ConfigurationPropertiesBindingPostProcessor.BEAN_NAME)) {
// ConfigurationPropertiesBindingPostProcessor注册
registerConfigurationPropertiesBindingPostProcessor(registry);
// ConfigurationBeanFactoryMetadata注册
registerConfigurationBeanFactoryMetadata(registry);
}
}

主要注册了两个beanDefinition,分别是ConfigurationPropertiesBindingPostProcessor类和ConfigurationBeanFactoryMetadata类。

看来具体的属性绑定就是这两个类来处理了,笔者继续往下分析

ConfigurationBeanFactoryMetadata

根据spring的bean的生命周期,实现BeanFactoryPostProcessor接口类中的postProcessBeanFactory()方法优先执行

	@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
this.beanFactory = beanFactory;
for (String name : beanFactory.getBeanDefinitionNames()) {
BeanDefinition definition = beanFactory.getBeanDefinition(name);
String method = definition.getFactoryMethodName();
String bean = definition.getFactoryBeanName();
if (method != null && bean != null) {
this.beansFactoryMetadata.put(name, new FactoryMetadata(bean, method));
}
}
}

其主要用于获取bean工厂上实现被@Bean注解修饰的方法的bean对象,表明@ConfigurationProperties也可以用于修饰method方法上用于动态注入

ConfigurationPropertiesBindingPostProcessor

根据spring的bean的生命周期,首先我们看下其afterPropertiesSet()实现方法

	@Override
public void afterPropertiesSet() throws Exception {
// 获取ConfigurationBeanFactoryMetadata实体类
this.beanFactoryMetadata = this.applicationContext.getBean(
ConfigurationBeanFactoryMetadata.BEAN_NAME,
ConfigurationBeanFactoryMetadata.class);
// 创建属性绑定类
this.configurationPropertiesBinder = new ConfigurationPropertiesBinder(
this.applicationContext, VALIDATOR_BEAN_NAME);
}

再而我们看下其postProcessBeforeInitialization()方法

	@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// 查找是否bean上对应的class上含有ConfigurationProperties注解
ConfigurationProperties annotation = getAnnotation(bean, beanName,
ConfigurationProperties.class);
if (annotation != null) {
// 开始绑定操作
bind(bean, beanName, annotation);
}
return bean;
}

Ok,我们继续往下追踪bind()方法

	private void bind(Object bean, String beanName, ConfigurationProperties annotation) {
ResolvableType type = getBeanType(bean, beanName);
// 查看类上是否含有@Validated注解
Validated validated = getAnnotation(bean, beanName, Validated.class);
Annotation[] annotations = (validated != null
? new Annotation[] { annotation, validated }
: new Annotation[] { annotation });
// class/实例/注解 三个绑定在一起供下述调用
Bindable<?> target = Bindable.of(type).withExistingValue(bean)
.withAnnotations(annotations);
try {
this.configurationPropertiesBinder.bind(target);
}
catch (Exception ex) {
throw new ConfigurationPropertiesBindException(beanName, bean, annotation,
ex);
}
}

最终的如何绑定本文就不讲解了,主要会读取Environment的属性然后进行对比绑定

涉及的代码很多,有兴趣的读者可自行分析。

所以基于上述的此加工类的作用,用户也可以直接通过@Bean注解创建Bean

// 直接返回实例,具体的内部属性的绑定则会由ConfigurationPropertiesBindingPostProcessor加工类完成
@Bean
public UserProperty userProperty(){
return new UserProperty();
}

小结

如果想在springboot中使用外部源的属性值有两个方法

  1. @PropertySource注解加载外部源,然后通过@Value注解来进行注入即可

  2. @ConfigurationProperties注解修饰javabean,其prefix属性是必须配置的,javabean的内部属性与配置文件指定的属性须一致,并且setter方法必须配置,这样可确保属性设置能成功

springboot情操陶冶-@ConfigurationProperties注解解析的更多相关文章

  1. springboot情操陶冶-@SpringBootApplication注解解析

    承接前文springboot情操陶冶-@Configuration注解解析,本文将在前文的基础上对@SpringBootApplication注解作下简单的分析 @SpringBootApplicat ...

  2. springboot情操陶冶-@Configuration注解解析

    承接前文springboot情操陶冶-SpringApplication(二),本文将在前文的基础上分析下@Configuration注解是如何一步一步被解析的 @Configuration 如果要了 ...

  3. SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器

    mvc:annotation-driven节点的解析器,是springmvc的核心解析器 官方注释 Open Declaration org.springframework.web.servlet.c ...

  4. springboot情操陶冶-@Conditional和@AutoConfigureAfter注解解析

    承接前文springboot情操陶冶-@Configuration注解解析,本文将在前文的基础上阐述@AutoConfigureAfter和@Conditional注解的作用与解析 1.@Condit ...

  5. springboot情操陶冶-jmx解析

    承接前文springboot情操陶冶-@Configuration注解解析,近期笔者接触的项目中有使用到了jmx的协议框架,遂在前文的基础上讲解下springboot中是如何整合jmx的 知识储备 J ...

  6. springboot情操陶冶-web配置(一)

    承接前文springboot情操陶冶-@SpringBootApplication注解解析,在前文讲解的基础上依次看下web方面的相关配置 环境包依赖 在pom.xml文件中引入web依赖,炒鸡简单, ...

  7. springboot情操陶冶-SpringApplication(二)

    承接前文springboot情操陶冶-SpringApplication(一),本文将对run()方法作下详细的解析 SpringApplication#run() main函数经常调用的run()方 ...

  8. springboot情操陶冶-web配置(七)

    参数校验通常是OpenApi必做的操作,其会对不合法的输入做统一的校验以防止恶意的请求.本文则对参数校验这方面作下简单的分析 spring.factories 读者应该对此文件加以深刻的印象,很多sp ...

  9. springboot情操陶冶-web配置(四)

    承接前文springboot情操陶冶-web配置(三),本文将在DispatcherServlet应用的基础上谈下websocket的使用 websocket websocket的简单了解可见维基百科 ...

随机推荐

  1. LOJ 6092

    这个题也很没意思 发现q那么大没有用, 不重复的询问有26*n种 所以记录一下就好了 #include<bits/stdc++.h> using namespace std; #defin ...

  2. c# asp.net mvc4 使用uploadify插件实现上传功能

    [1]首先去官网下载插件:http://www.uploadify.com/download/ .ww我使用的是免费的,基于flash的版本.因为基于H5的版本需付费使用,然后使用该插件也就是做做毕设 ...

  3. 逛公园 [NOIP2017 D1T3] [记忆化搜索]

    Description 策策同学特别喜欢逛公园.公园可以看成一张N个点M条边构成的有向图,且没有自环和重边.其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的 ...

  4. centos7安装kubeadm

    安装配置docker v1.9.0版本推荐使用docker v1.12, v1.11, v1.13, 17.03也可以使用,再高版本的docker可能无法正常使用. 测试发现17.09无法正常使用,不 ...

  5. 再回首数据结构—数组(Golang实现)

    数组为线性数据结构,通常编程语言都有自带了数组数据类型结构,数组存放的是有个相同数据类型的数据集: 为什么称数组为线性数据结构:因为数组在内存中是连续存储的数据结构,数组中每个元素最多只有左右两个方向 ...

  6. vue百度地图插件

    安装 npm i --save vue-baidu-map 代码 <template> <div> <baidu-map v-bind:style="mapSt ...

  7. webpack学习最基本的使用方式(一)

    网页中引入的静态资源多了以后会有什么问题.? 1.网页加载速度慢,因为我们要发起很多的二次请求 2.要处理错综复杂的依赖关系 如何解决上面的问题 1.合并,压缩图片,使用精灵图 2.可以使用之前学过的 ...

  8. VS2017Release+x64失败,LNK1104,无法打开文件"msvcprt.lib"

    采用VS2017+Qt5.10联合开发环境建立开发,将Qt的库包含到VS中使用VS2017的Debug+x64模式调试程序,通过并出现对应的EXE应用程序! 但是转换到Release+x64模式出现问 ...

  9. Qt5和VS2017建立开发环境,安装后新建项目找不到Qt选项!!!

    最近开发win驱动和Qt5测试程序,需要建立Qt5和VS2017开发环境---对于Qt5和VS2017安装这里不做多余叙述. 参考资源很多,讲解也不错!! 这里切入正题:在VS2017中安转Qt vs ...

  10. Visual Studio(VS)秘钥集合

    Visual Studio 2019 Pro :NYWVH-HT4XC-R2WYW-9Y3CM-X4V3Y