spring 源码系列(三)- PropertySource和Environment

注:本章内容主要是作为springboot的run()方法中的环境预准备prepareEnvironment()方法的预备知识,其中本章的environment部分可能比较抽象,不是很好理解,这并没有什么问题,只要大概明白即可,后续阅读prepareEnvironment()方法时会加深理解。

1 @PropertySource

1.1 使用

PropertySource是spring体系用于存放key-value的property属性的基本单位
最常见的使用方法是与@Value搭配:

@Component
@PropertySource("classpath:db.properties")
public class DBConnection {

    @Value("${db.driver}")
    private String driverClass;

    @Value("${db.url}")
    private String dbUrl;

    @Value("${db.username}")
    private String userName;

    @Value("${db.password}")
    private String password;

    public DBConnection(){}

    public void printDBConfigs(){
        System.out.println("Db Driver Class = " + driverClass);
        System.out.println("Db url = " + dbUrl);
        System.out.println("Db username = " + userName);
        System.out.println("Db password = " + password);
    }
}

db.properties:

db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/test
db.username=cxuan
db.password=111111

main函数:

    public static void main(String[] args){
        ApplicationContext context = new AnnotationConfigApplicationContext(DBConnection.class);
        DBConnection bean = context.getBean(DBConnection.class);
        bean.printDBConfigs();
    }

1.2 原理

最重要的后置处理器ConfigurationClassPostProcessor会对@Configuration等注释类进行解析,这其中就包含了@PropertySource类,该类的解析具体在doProcessConfigurationClass()方法的第二步中:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
		throws IOException {

	// 1、首先处理内部类,处理内部类时,最终还是调用doProcessConfigurationClass()方法
	processMemberClasses(configClass, sourceClass);
	// 2、处理属性资源文件,加了@PropertySource注解
	for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), PropertySources.class,
			org.springframework.context.annotation.PropertySource.class)) {
		if (this.environment instanceof ConfigurableEnvironment) {
			processPropertySource(propertySource);
		}
	}
	...
}

可以看到,解析@PropertySource调用的是processPropertySource():

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
		//取出该注释的name:对于DBConnection类,name为“”(没有设置)
		String name = propertySource.getString("name");
		if (!StringUtils.hasLength(name)) {
			name = null;
		}
		//..获取encoding,代码略,同样为空
		//获取注释的value,对于DBConnection类,是"classpath:db.properties"
		String[] locations = propertySource.getStringArray("value");
		//..略
		
		//获取执行工厂,默认为PropertySourceFactory
		Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
		PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
				DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

		//递归locations中的所有文件,每个文件都生成Resource类,
		//然后调用factory.createPropertySource()生成PropertySource类,再添加到environment.propertySources中。
		for (String location : locations) {
			try {
				String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
				Resource resource = this.resourceLoader.getResource(resolvedLocation);
				addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
			}
		//...略
	}

可以看到,@PropertySource注释的类,经过解析后,会转化成PropertySource类,并在最后存入environment的propertySources中

而关于Environment、PropertySource和propertySources的内容,后续会进行详细介绍。

2 PropertySource

2.1 PropertySource

PropertySource是一个用于存储key和value的类,用于存储spring环境中的配置信息,例如application.properties或application.yml就是用的这个类。
代码:

public abstract class PropertySource<T> {
    protected final Log logger;
    //key
    protected final String name;
    //value
    protected final T source;

    public PropertySource(String name, T source) {
        this.logger = LogFactory.getLog(this.getClass());
        Assert.hasText(name, "Property source name must contain at least one character");
        Assert.notNull(source, "Property source must not be null");
        this.name = name;
        this.source = source;
    }

对于application.properties, 每一行数据对应着一个PropertySource。

2.2 PropertySources

PropertySources是存储PropertySource的通用接口,从下面的代码中可以看到,其继承了Iterable<PropertySource<?>>类,这样就具有了迭代PropertySource的功能。

public interface PropertySources extends Iterable<PropertySource<?>> {
    boolean contains(String var1);

    PropertySource<?> get(String var1);
}

MutablePropertySources实现了PropertySources接口,使用了List中的CopyOnWriteArrayList来存储PropertySource:

public class MutablePropertySources implements PropertySources {
    private final Log logger;
    private final List<PropertySource<?>> propertySourceList;

    public MutablePropertySources() {
        this.propertySourceList = new CopyOnWriteArrayList();
        this.logger = LogFactory.getLog(this.getClass());
    }
	
	//...略
}

3 Environment

Environment是spring体系中,用于存储配置信息的接口/类。
实际上,Environment=profiles+properties

3.1 environment和PropertySources

AbstractEnvironment类将MutablePropertySources类型的propertySources作为成员变量,因此AbstractEnvironment及子类都有了propertySources:

public abstract class AbstractEnvironment implements ConfigurableEnvironment {
	...
	private final MutablePropertySources propertySources;
	...
}

因此,environment中可以存入多个propertySource。

3.2 environment体系

关于Environment,传送门:
Spring源码系列 — Envoriment组件(只需要了解每个类大概是干什么的就行,不需要太细致)。
在这里插入图片描述
因为上面的文章中没有对StandardServletEnvironment进行讲解,因此在这里补充:

3.3 StandardServletEnvironment

StandardServletEnvironment相对于父类StandardEnvironment,会再设置两个PropertySource:

  1. servletConfigInitParams
  2. servletContextInitParams
    这里不对这两个PropertySource进行讲解,以后用到时再去研究。
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {

    public StandardServletEnvironment() {
    }

	//构造该类时会调用customizePropertySources()
    protected void customizePropertySources(MutablePropertySources propertySources) {
    	//配置servletConfigInitParams
        propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
        //配置servletContextInitParams
        propertySources.addLast(new StubPropertySource("servletContextInitParams"));
        //配置JNDI,这里会用到SpringProperties类,略
        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
            propertySources.addLast(new JndiPropertySource("jndiProperties"));
        }

        super.customizePropertySources(propertySources);
   }

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值