spring Environment 、PropertySource、 PropertyResolver学习

PropertySource:属性源,key-value属性对抽象,比如用于配置数据

PropertyResolver:属性解析器,用于解析相应key的value

Environment:环境,本身是一个PropertyResolver,但是提供了Profile特性,即可以根据环境得到相应数据(即激活不同的Profile,可以得到不同的属性数据,比如用于多环境场景的配置(正式机、测试机、开发机DataSource配置))

Profile:剖面,只有激活的剖面的组件/配置才会注册到Spring容器

PropertySource

PropertySource有很多实现类

MapPropertySource的属性来自于一个Map,而ResourcePropertySource的属性来自于一个properties文件,另外还有如PropertiesPropertySource,其属性来自Properties,ServletContextPropertySource的属性来自ServletContext上下文初始化参数等等,大家可以查找PropertySource的继承层次查找相应实现。

 

MapPropertySource的属性来自于一个Map,而ResourcePropertySource的属性来自于一个properties文件,另外还有如PropertiesPropertySource,其属性来自Properties,ServletContextPropertySource的属性来自ServletContext上下文初始化参数等等,大家可以查找PropertySource的继承层次查找相应实现。

 

    @Test
    public void test() throws IOException {
        Map<String, Object> map = new HashMap<>();
        map.put("encoding", "gbk");
        PropertySource propertySource1 = new MapPropertySource("map", map);
        System.out.println(propertySource1.getProperty("encoding"));

        ResourcePropertySource propertySource2 = new ResourcePropertySource("resource", "classpath:resources.properties"); //name, location
        System.out.println(propertySource2.getProperty("encoding"));
    }

 

    @Test
    public void test3() throws IOException {
        //省略propertySource1/propertySource2
        MutablePropertySources propertySources = new MutablePropertySources();
        propertySources.addFirst(propertySource1);
        propertySources.addLast(propertySource2);
        System.out.println(propertySources.get("resource").getProperty("encoding"));

        for(PropertySource propertySource : propertySources) {
            System.out.println(propertySource.getProperty("encoding"));
        }
    }

 

默认提供了一个MutablePropertySources实现,我们可以调用addFirst添加到列表的开头,addLast添加到末尾,另外可以通过addBefore(propertySourceName, propertySource)或addAfter(propertySourceName, propertySource)添加到某个propertySource前面/后面;最后大家可以通过iterator迭代它,然后按照顺序获取属性。

 

到目前我们已经有属性了,接下来需要更好的API来解析属性了。

PropertyResolver

属性解析器,用来根据名字解析其值等。API如下所示:

public interface PropertyResolver {

	//是否包含某个属性
	boolean containsProperty(String key);
 
        //获取属性值 如果找不到返回null 
	String getProperty(String key);
     
        //获取属性值,如果找不到返回默认值   	
	String getProperty(String key, String defaultValue);
  
        //获取指定类型的属性值,找不到返回null
	<T> T getProperty(String key, Class<T> targetType);

        //获取指定类型的属性值,找不到返回默认值
	<T> T getProperty(String key, Class<T> targetType, T defaultValue);

         //获取属性值为某个Class类型,找不到返回null,如果类型不兼容将抛出ConversionException
	<T> Class<T> getPropertyAsClass(String key, Class<T> targetType);

        //获取属性值,找不到抛出异常IllegalStateException
	String getRequiredProperty(String key) throws IllegalStateException;

        //获取指定类型的属性值,找不到抛出异常IllegalStateException       
	<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

        //替换文本中的占位符(${key})到属性值,找不到不解析
	String resolvePlaceholders(String text);

        //替换文本中的占位符(${key})到属性值,找不到抛出异常IllegalArgumentException
	String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

}

 

    @Test
    public void test() throws Exception {
        //省略propertySources

        PropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources);

        System.out.println(propertyResolver.getProperty("encoding"));
        System.out.println(propertyResolver.getProperty("no", "default"));
        System.out.println(propertyResolver.resolvePlaceholders("must be encoding ${encoding}"));  //输出must be encoding gbk
    }

 

从如上示例可以看出其非常简单。另外Environment也继承了PropertyResolver。

Environment

 

 环境,比如JDK环境,Servlet环境,Spring环境等等;每个环境都有自己的配置数据,如System.getProperties()、System.getenv()等可以拿到JDK环境数据;ServletContext.getInitParameter()可以拿到Servlet环境配置数据等等;也就是说Spring抽象了一个Environment来表示环境配置。

public interface Environment extends PropertyResolver {//继承PropertyResolver

        //得到当前明确激活的剖面
	String[] getActiveProfiles();

        //得到默认激活的剖面,而不是明确设置激活的
	String[] getDefaultProfiles();
 
        //是否接受某些剖面
	boolean acceptsProfiles(String... profiles);

}

 从API上可以看出,除了可以解析相应的属性信息外,还提供了剖面相关的API,目的是: 可以根据剖面有选择的进行注册组件/配置。比如对于不同的环境注册不同的组件/配置(正式机、测试机、开发机等的数据源配置)。它的主要几个实现如下所示:

 

MockEnvironment:模拟的环境,用于测试时使用;

StandardEnvironment:标准环境,普通Java应用时使用,会自动注册System.getProperties() 和 System.getenv()到环境;

StandardServletEnvironment:标准Servlet环境,其继承了StandardEnvironment,Web应用时使用,除了StandardEnvironment外,会自动注册ServletConfig(DispatcherServlet)、ServletContext及JNDI实例到环境;

除了这些,我们也可以根据需求定义自己的Environment。示例如下:

    @Test
    public void test() {
        //会自动注册 System.getProperties() 和 System.getenv()
        Environment environment = new StandardEnvironment();
        System.out.println(environment.getProperty("file.encoding"));
    }

 其默认有两个属性:systemProperties(System.getProperties())和systemEnvironment(System.getenv())

使用StandardServletEnvironment加载时,默认除了StandardEnvironment的两个属性外,还有另外三个属性:servletContextInitParams(ServletContext)、servletConfigInitParams(ServletConfig)、jndiProperties(JNDI)。

实现方式:在AbstractEnvironment的构造方法中,提供模板方法customizePropertySources(this.propertySources);子类去相应实现

猜你喜欢

转载自cainiaoboke.iteye.com/blog/2327755
今日推荐