@ConfigurationProperties 使用方式有两种
1、在类上使用该注解
2、在工厂方法上使用该注解 (@bean)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {
@AliasFor("prefix")
String value() default "";
@AliasFor("value")
String prefix() default "";
boolean ignoreInvalidFields() default false;
boolean ignoreNestedProperties() default false;
boolean ignoreUnknownFields() default true;
/** @deprecated */
@Deprecated
boolean exceptionIfInvalid() default true;
}
方式1:
@Getter
@Setter
@Configuration
@PropertySource("classpath:db.properties")
@ConfigurationProperties(prefix = "spring.datasource.sakila")
public class DbConfigInfo {
private String url;
private String username;
private String password;
private String driverClassName;
private String initialSize;
private String maxIdle;
private String minIdle;
}
方式2:
@Configuration
@PropertySource("classpath:db.properties")
public class DBConfig {
@Primary
@Bean("libraryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.library")
public DataSource sakilaDataSource() {
return DataSourceBuilder.create().build();
}
@Bean("sakilaDataSource")
@ConfigurationProperties(prefix = "spring.datasource.sakila")
public DataSource libraryDataSource() {
return DataSourceBuilder.create().build();
}
}
SpringBoot 是如何通過@ConfigurationProperties來获取配置信息的呢?
我们知道,SpringBoot在启动时默认会加载一些类,其中包含了AbstractAutowireCapableBeanFactory, 从而触发了applyBeanPostProcessorsBeforeInitialization接口,applyBeanPostProcessorsBeforeInitialization接口中调用了ConfigurationPropertiesBindingPostProcessor的postProcessBeforeInitialization接口。
applyBeanPostProcessorsBeforeInitialization接口:
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
Iterator var4 = this.getBeanPostProcessors().iterator();
do {
if (!var4.hasNext()) {
return result;
}
BeanPostProcessor beanProcessor = (BeanPostProcessor)var4.next();
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
} while(result != null);
return result;
}
postProcessBeforeInitialization接口:若存在ConfigurationProperties注解,则执行postProcessBeforeInitialization接口
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
ConfigurationProperties annotation = (ConfigurationProperties)AnnotationUtils.findAnnotation(bean.getClass(), ConfigurationProperties.class); //方式一
if (annotation != null) {
this.postProcessBeforeInitialization(bean, beanName, annotation);
}
annotation = (ConfigurationProperties)this.beans.findFactoryAnnotation(beanName, ConfigurationProperties.class);//方式二
if (annotation != null) {
this.postProcessBeforeInitialization(bean, beanName, annotation);
}
return bean;
}
postProcessBeforeInitialization接口:
private void postProcessBeforeInitialization(Object bean, String beanName, ConfigurationProperties annotation) {
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory(bean);
factory.setPropertySources(this.propertySources);
factory.setValidator(this.determineValidator(bean));
factory.setConversionService(this.conversionService == null ? this.getDefaultConversionService() : this.conversionService);
if (annotation != null) {
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
if (StringUtils.hasLength(annotation.prefix())) {
factory.setTargetName(annotation.prefix());
}
}
try {
factory.bindPropertiesToTarget(); //进行属性绑定
} catch (Exception var8) {
String targetClass = ClassUtils.getShortName(bean.getClass());
throw new BeanCreationException(beanName, "Could not bind properties to " + targetClass + " (" + this.getAnnotationDetails(annotation) + ")", var8);
}
}
通过PropertiesConfigurationFactory的bindPropertiesToTarget进行属性绑定:target是需要进行属性绑定的object
public void bindPropertiesToTarget() throws BindException {
Assert.state(this.propertySources != null, "PropertySources should not be null");
try {
if (logger.isTraceEnabled()) {
logger.trace("Property Sources: " + this.propertySources);
}
this.hasBeenBound = true;
this.doBindPropertiesToTarget();
} catch (BindException var2) {
if (this.exceptionIfInvalid) {
throw var2;
}
logger.error("Failed to load Properties validation bean. Your Properties may be invalid.", var2);
}
}
private void doBindPropertiesToTarget() throws BindException {
RelaxedDataBinder dataBinder = this.targetName != null ? new RelaxedDataBinder(this.target, this.targetName) : new RelaxedDataBinder(this.target);
if (this.validator != null && this.validator.supports(dataBinder.getTarget().getClass())) {
dataBinder.setValidator(this.validator);
}
if (this.conversionService != null) {
dataBinder.setConversionService(this.conversionService);
}
dataBinder.setAutoGrowCollectionLimit(2147483647);
dataBinder.setIgnoreNestedProperties(this.ignoreNestedProperties);
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
this.customizeBinder(dataBinder);
Iterable<String> relaxedTargetNames = this.getRelaxedTargetNames();
Set<String> names = this.getNames(relaxedTargetNames);
PropertyValues propertyValues = this.getPropertySourcesPropertyValues(names, relaxedTargetNames);
dataBinder.bind(propertyValues); //属性绑定
if (this.validator != null) {
dataBinder.validate();
}
this.checkForBindingErrors(dataBinder);
}