zoukankan      html  css  js  c++  java
  • Spring Environment(一)API 介绍

    Spring Environment(一)API 使用

    Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

    Spring 3.1 提供了新的属性管理 API,而且功能非常强大且很完善,对于一些属性配置信息都应该使用新的 API 来管理。位于 org.springframework.core.env 包内。

    Spring Environment 属性配置管理系列文章:

    1. Spring Environment(一)API 介绍
    2. Spring Environment(二)源码分析
    3. Spring Environment(三)生命周期

    一、新的属性管理 API

    • PropertySource:属性源,key-value 属性对抽象,比如用于配置数据
    • PropertyResolver:属性解析器,用于解析相应 key 的 value
    • Environment:环境,本身是一个 PropertyResolver,但是提供了 Profile 特性,即可以根据环境得到相应数据(即激活不同的 Profile,可以得到不同的属性数据,比如用于多环境场景的配置(正式机、测试机、开发机 DataSource 配置)
    • Profile:剖面,只有激活的剖面的组件/配置才会注册到 Spring 容器,类似于 maven 中 profile

    也就是说,新的 API 主要从配置属性、解析属性、不同环境解析不同的属性、激活哪些组件/配置进行注册这几个方面进行了重新设计,使得 API 的目的更加清晰,而且功能更加强大。

    @Test
    public void test() {
        Environment env = new StandardEnvironment();
        // 1. 操作系统的环境变量
        Map<String, Object> systemEnvironment = ((StandardEnvironment) env).getSystemEnvironment();
        Assert.assertNotNull(systemEnvironment);
    
        // 2. JVM 属性配置
        Map<String, Object> systemProperties = ((StandardEnvironment) env).getSystemProperties();
        Assert.assertNotNull(systemProperties);
    
        // 3. 属性
        Assert.assertEquals("UTF-8", env.getProperty("file.encoding"));
        Assert.assertTrue(env.containsProperty("file.encoding"));
    
        // 4. 剖面 spring.profiles.default(默认为 default) spring.profiles.active
        //    只要有一个返回 true acceptsProfiles 方法就返回 true,!a 为不包含该 profiles
        Assert.assertTrue(env.acceptsProfiles("default"));
        Assert.assertTrue(env.acceptsProfiles("a", "default"));
        Assert.assertFalse(env.acceptsProfiles("a"));
        Assert.assertTrue(env.acceptsProfiles("!a", "b"));
    }
    

    二、PropertySource

    PropertySource 类图

    public abstract class PropertySource<T> {
        // 给数据源起个名称
        protected final String name;
        // 数据源,可能为 Map 或 Properties ...
        protected final T source;
    
        public boolean containsProperty(String name) {
            return (getProperty(name) != null);
        }
        public abstract Object getProperty(String name);
    }
    

    PropertySource 非常类似于 Map,数据源可来自 Map、Properties、Resource 等。PropertySource 接口有两个特殊的子类:StubPropertySource 用于占位用,ComparisonPropertySource 用于集合排序,不允许获取属性值。

    2.1 PropertySource 实现类

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

    @Test
    public void PropertySourceTest() throws IOException {
        PropertySource mapPropertySource = new MapPropertySource("map",
                Collections.singletonMap("key", "source1"));
        Assert.assertEquals("value1", mapPropertySource.getProperty("key"));
    
        ResourcePropertySource resourcePropertySource = new ResourcePropertySource(
                "resource", "classpath:resources.properties");
        Assert.assertEquals("value2", resourcePropertySource.getProperty("key"));
    }
    

    2.2 CompositePropertySource

    CompositePropertySource 提供了组合 PropertySource 的功能,查找顺序就是注册顺序。

    @Test
    public void CompositePropertySourceTest() throws IOException {
    
        PropertySource propertySource1 = new MapPropertySource("source1",
                Collections.singletonMap("key", "value1"));
        PropertySource propertySource2 = new MapPropertySource("source2",
                Collections.singletonMap("key", "value2"));
    
        CompositePropertySource compositePropertySource = new CompositePropertySource("composite");
        compositePropertySource.addPropertySource(propertySource1);
        compositePropertySource.addPropertySource(propertySource2);
        Assert.assertEquals("value1", compositePropertySource.getProperty("key"));
    }
    

    2.3 PropertySources

    PropertySources 类图

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

    注意:PropertySource 的顺序非常重要,因为 Spring 只要读到属性值就返回。

    @Test
    public void PropertySourcesTest() throws IOException {
        PropertySource propertySource1 = new MapPropertySource("source1",
                Collections.singletonMap("key", "value1"));
        PropertySource propertySource2 = new MapPropertySource("source2",
                Collections.singletonMap("key", "value2"));
    
        MutablePropertySources propertySources = new MutablePropertySources();
        propertySources.addFirst(propertySource1);
        propertySources.addLast(propertySource2);
    
        Assert.assertEquals("value1", propertySources.get("source1").getProperty("key"));
        Assert.assertEquals("value2", propertySources.get("source2").getProperty("key"));
    }
    

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

    三、PropertyResolver

    PropertyResolver 的使用参考:https://www.cnblogs.com/binarylei/p/10284826.html

    四、Environment

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

    public interface Environment extends PropertyResolver {
        String[] getActiveProfiles();
        String[] getDefaultProfiles();
    
        // @since 5.1 废弃,改用 Profiles(Profiles.of("dev"))
        @Deprecated
        boolean acceptsProfiles(String... profiles);
        boolean acceptsProfiles(Profiles profiles);
    }
    

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

    • MockEnvironment:模拟的环境,用于测试时使用;
    • StandardEnvironment:标准环境,普通 Java 应用时使用,会自动注册 System.getProperties() 和 System.getenv()到环境;
    • StandardServletEnvironment:标准 Servlet 环境,其继承了 StandardEnvironment,Web 应用时使用,除了 StandardEnvironment 外,会自动注册 ServletConfig(DispatcherServlet)、ServletContext 及 JNDI 实例到环境;

    4.1 web.xml 配置 Servlet 属性

    <context-param>  
        <param-name>myConfig</param-name>  
        <param-value>hello</param-value>  
    </context-param>  
      
    <servlet>  
        <servlet-name>spring</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>classpath:spring-mvc.xml</param-value>  
        </init-param>  
    </servlet>  
    

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

    4.2 Environment 获取

    (1) 注解

    @Autowired  
    Environment env;  
    

    (2) ApplicationContext

    applicationContext.getEnvironment();
    

    五、Profile

    profile 剖面,大体意思是:我们程序可能从某几个剖面来执行应用,比如正式机环境、测试机环境、开发机环境等,每个剖面的配置可能不一样(比如开发机可能使用本地的数据库测试,正式机使用正式机的数据库测试)等;因此呢,就需要根据不同的环境选择不同的配置;如果 maven 中的 profile 的概念。

    profile 有两种:

    • 默认的:通过 "spring.profiles.default" 属性获取,如果没有配置默认值是 "default"
    • 明确激活的:通过 "spring.profiles.active" 获取

    查找顺序是:先进性明确激活的匹配,如果没有指定明确激活的(即集合为空)就找默认的;配置属性值从 Environment 读取。

    profile 设置方式常见的有三种:

    (1) -D 传入系统参数

    -Dspring.profiles.active=dev  
    

    (2) web 环境

    <context-param>  
        <param-name>spring.profiles.active</param-name>  
        <param-value>dev</param-value>  
    </context-param>  
    

    (3) xml 配置

    通过在 beans 标签上加上 profile 属性,这样当我们激活相应的 profile 时,此 beans 标签下的 bean 就会注册,如下所示:

    <beans>  
        <beans profile="dev">  
            <bean id="dataSource" class="...">  
            </bean>              
        </beans>  
          
        <beans profile="test">  
            <bean id="dataSource" class="...">  
            </bean>  
        </beans>   
    </beans>  
    

    启动应用时设置相应的 "spring.profiles.active" 即可。另外,如果想指定一个默认的,可以使用 指定(如果不是 default,可以通过 "spring.profiles.default" 指定)。

    (4) 注解配置

    Java Config 方式的 Profile,功能等价于 XML 中的 ,使用方式如下:

    @Profile("dev")  
    @Configuration  
    @PropertySource(value = "classpath:resources.properties", ignoreResourceNotFound = false)  
    public class AppConfig {  
      
        @Bean  
        public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {  
            return new PropertySourcesPlaceholderConfigurer();  
        }  
    }  
    

    Spring4 提供了一个新的 @Conditional 注解,请参考 http://jinnianshilongnian.iteye.com/blog/1989379

    (5) @ActiveProfiles()

    在测试时,有时候不能通过系统启动参数/上下文参数等指定 Profile,此时 Spring 测试框架提供了 @ActiveProfiles() 注解,示例如下:

    @ActiveProfiles("test")  
    @RunWith(SpringJUnit4ClassRunner.class)  
    @ContextConfiguration(classes = GenericConfig.class)  
    public class GenricInjectTest {  
    }  
    

    到此整个 Spring 的属性管理 API 就介绍完了,对于属性管理,核心是 Environment。

    参考:

    1. 《pring3.1新属性管理API:PropertySource、Environment、Profile》:https://jinnianshilongnian.iteye.com/blog/2000183

    每天用心记录一点点。内容也许不重要,但习惯很重要!

  • 相关阅读:
    spring cloud 和 阿里微服务spring cloud Alibaba
    为WPF中的ContentControl设置背景色
    java RSA 解密
    java OA系统 自定义表单 流程审批 电子印章 手写文字识别 电子签名 即时通讯
    Hystrix 配置参数全解析
    spring cloud 2020 gateway 报错503
    Spring Boot 配置 Quartz 定时任务
    Mybatis 整合 ehcache缓存
    Springboot 整合阿里数据库连接池 druid
    java OA系统 自定义表单 流程审批 电子印章 手写文字识别 电子签名 即时通讯
  • 原文地址:https://www.cnblogs.com/binarylei/p/10280374.html
Copyright © 2011-2022 走看看