Spring中的@PropertySource注解源码详解
作者:drunk in spring
@PropertySource注解使用
@PropertySource注解用于指定资源文件读取的位置,它不仅能读取properties文件,也能读取xml文件,并且通过yaml解析器,配合自定义PropertySourceFactory实现解析yaml文件
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
//资源名称,为空则根据资源的描述符生成
String name() default "";
/**
*资源路径
*classpath:application.properties
*file:/
*/
String[] value();
//是否忽略资源不存在的情况,如果不忽略,当资源不存在时报错
boolean ignoreResourceNotFound() default false;
//指定资源文件的编码格式
String encoding() default "";
//资源工厂
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}源码解析
在bean实例化之前有一个ConfigurationClassPostProcessor类,会操作BeanDefinition并且加入到BeanDefinitionRegistry中,它的优先级在所有的BeanDefinitionRegistryPostProcessor里面是最低的。
优先级:最低
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE; // within PriorityOrdered
}
会在这个类中进行相关注解的解析操作,在进行@PropertySource注解解析的时候要借助ConfigurationClassParser的parse方法
在进行@PropertySource注解解析之前,需要拿到两个对象ConfigurationClass和SourceClass
bean的元数据信息(bd.getMetadata),和beanName,封装成ConfigurationClass对象
new ConfigurationClass(metadata, beanName)
SourceClass这个对象理解为跟类或者接口对应,然后把metadata对象包装进去
SourceClass sourceClass = asSourceClass(configClass, filter);
接下来正式进入解析流程
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
//核心逻辑
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
拿到类上面的所有PropertySource注解的值AnnotationAttributes,放到processPropertySource方法中进行解析
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
//获取配置文件路径
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
//替换占位符
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
//流的方式加载配置文件并封装成Resource对象
Resource resource = this.resourceLoader.getResource(resolvedLocation);
//加载Resource中的配置属性封装成Properties对象中,并创建PropertySource对象加入到Environment对象中
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
// Placeholders not resolvable or resource not found when trying to open it
if (ignoreResourceNotFound) {
if (logger.isInfoEnabled()) {
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
这里看一下createPropertySource方法,就是将配置文件中的内容封装成ResourcePropertySource对象,作为属性源
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}
属性源拿到之后,要统一交给Environment管理,调用addPropertySource方法
private void addPropertySource(PropertySource<?> propertySource) {
String name = propertySource.getName();
//获取Environment对象中的MutablePropertySources
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
//如果已经存在了该配置文件的PropertySource则合并久的
if (this.propertySourceNames.contains(name)) {
// We've already added a version, we need to extend it
PropertySource<?> existing = propertySources.get(name);
if (existing != null) {
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
((ResourcePropertySource) propertySource).withResourceName() : propertySource);
//合并二次后的类型
if (existing instanceof CompositePropertySource) {
((CompositePropertySource) existing).addFirstPropertySource(newSource);
}
else {
if (existing instanceof ResourcePropertySource) {
existing = ((ResourcePropertySource) existing).withResourceName();
}
//其实就是CompositePropertySource里面有一个Set,Set里面装了新和旧的PropertySource对象
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
propertySources.replace(name, composite);
}
return;
}
}
if (this.propertySourceNames.isEmpty()) {
propertySources.addLast(propertySource);
}
else {
//用于计算插入的位置index
String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
//吧propertySource对象存入MutablePropertySources的list中
propertySources.addBefore(firstProcessed, propertySource);
}
this.propertySourceNames.add(name);
}
拿到Environment中的MutablePropertySources,用来放置拿到的属性源。 如果有相同的属性源,则升级成CompositePropertySource,把新旧相同的属性源进行合并,再放到MutablePropertySources中
看看CompositePropertySource类的内部 有Set<PropertySource<?>> propertySources = new LinkedHashSet<>();用来放置属性源 也重写了getProperty方法
public Object getProperty(String name) {
for (PropertySource<?> propertySource : this.propertySources) {
Object candidate = propertySource.getProperty(name);
if (candidate != null) {
return candidate;
}
}
return null;
}
@PropertySource注解中配置的属性源都交给Environment管理
到此这篇关于Spring中的@PropertySource注解源码详解的文章就介绍到这了,更多相关@PropertySource注解源码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
