之前文章简单的介绍了一下@Value和@PropertySource注解的使用,没有看过的同学可以点击查看:
一分钟学会spring注解之@value注解
一分钟学会spring注解之@PropertySource注解
今天这篇文章将给大家详细的介绍一下@PropertySource注解实现原理
首先让我们一起看下@PropertySource的源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
    /**
     * 资源的名称
     */
    String name() default "";
    /**
     * 资源文件路径,可以是数据多个文件地址
     * 可以是classpath地址如:
     *                  "classpath:/com/myco/app.properties"
     * 也可以是对应的文件系统地址如:
     *                  "file:/path/to/file"
     */
    String[] value();
    /**
     * 是否忽略文件资源是否存在,默认是false,也就是说配置不存在的文件地址spring启动将会报错
     */
    boolean ignoreResourceNotFound() default false;
    /**
     * 这个没什么好说的了就是对应的字符编码了,默认是空值,如果配置文件中有中文应该设置为utf-8     */
    String encoding() default "";
    /**
     * 关键的元素了 读取对应资源文件的工厂类了 默认的是PropertySourceFactory
     */
    Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
注意看上面代码中的注释,之前文章有演示过读取classpath中的配置文件,这边演示一下如何读取系统目录中文件如下:
@PropertySource(value={"classpath:/user2.properties","file:/d://user2.properties"},encoding="utf-8",ignoreResourceNotFound=true)
d盘中的user2.properties的配置文件如下:
u.name2=王五
u.age2=25
增加一个user1对象如下:
/**
* 用户名
 */
@Value("${u.name2}")
private String userName;
/**
* 年龄
*/
@Value("${u.age2}")
private Integer age;
运行测试如下:
实例1 === User [userName=李四, age=29]
实例2 === User [userName=王五, age=25]
从上我们可以发现@PropertySource注解的地址可以是以下两种:
- classpath路径:"classpath:/com/myco/app.properties" 
- 文件对应路径:"file:/path/to/file" 
接下来我们来详细的介绍@PropertySource注解底层是如何解析这些配置文件,这个就必须得PropertySourceFactory的具体实现源码了
进入PropertySourceFactory中你会发现它是一个接口代码如下:
public interface PropertySourceFactory {
    /**
     * Create a {@link PropertySource} that wraps the given resource.
     * @param name the name of the property source
     * @param resource the resource (potentially encoded) to wrap
     * @return the new {@link PropertySource} (never {@code null})
     * @throws IOException if resource resolution failed
     */
    PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException;
}
里边只有一个createPropertySource方法,进入其中的实现类中如下:
public class DefaultPropertySourceFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
    }
}
注意,重要的类ResourcePropertySource出现了,进去可以看到两个主要的构造方法如下:
/**
     * Create a PropertySource having the given name based on Properties
     * loaded from the given encoded resource.
     */
    public ResourcePropertySource(String name, EncodedResource resource) throws IOException {
        super(name, PropertiesLoaderUtils.loadProperties(resource));
        this.resourceName = getNameForResource(resource.getResource());
    }
    /**
     * Create a PropertySource based on Properties loaded from the given resource.
     * The name of the PropertySource will be generated based on the
     * {@link Resource#getDescription() description} of the given resource.
     */
    public ResourcePropertySource(EncodedResource resource) throws IOException {
        super(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource));
        this.resourceName = null;
    }
在构造方法中你可以发现加载资源的地方PropertiesLoaderUtils.loadProperties(resource),一路进去你可以返现如下代码:
static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister)
            throws IOException {
        InputStream stream = null;
        Reader reader = null;
        try {
            String filename = resource.getResource().getFilename();
            // 加载xml文件
            if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
                stream = resource.getInputStream();
                persister.loadFromXml(props, stream);
            }
            // 判断是否有需要对应的字符编码设置  有的话处理对应的InputStream
            else if (resource.requiresReader()) {
                reader = resource.getReader();
                persister.load(props, reader);
            }
            else {
                stream = resource.getInputStream();
                persister.load(props, stream);
            }
        }
}
怎么样,是不是可以发现@PropertySource不仅可以解析properties的文件同样也可以解析xml文件,下边我们一起来演示一下解析xml的例子吧
首先新增一个user2.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 
<properties>
    <entry key="u.name3">王二小</entry> 
    <entry key="u.age3">22</entry> 
</properties>
配置类增加配置如下:
@PropertySource(value={"classpath:/user.properties","classpath:/user2.xml","file:/d://user2.properties"},encoding="utf-8",ignoreResourceNotFound=false)
测试运行结果如下:
实例1 === User [userName=李四, age=29]
实例2 === User [userName=王二小, age=22]
好了,到目前为止我们不仅学会了@PropertySource注解的使用,而且了解到了其底层的具体实现,做到知其然知其所以然,以及了解了其默认的资源解析器PropertySourceFactory,并且你也可以继承PropertySourceFactory实现自定义的解析器,感兴趣的同学可以自己去实现一个自定义解析类
热门评论
- 
			
				qq_续写不尽的未来_02019-03-20 0 
 
 
查看全部评论您好,我在使用PropertySource注解时遇到了一个这样的问题,我在config目录下新建了test.properties文件,里面存放了test.aa="aa"这个属性,与此同时,我在默认的application.properties文件中存放了test.aa="11"这个属性,当我通过PropertySource+@value注解读取test.properties文件中这个值的时候,每次都是返回的11,若我去除application.properties中的这个属性,则返回的是aa