2019独角兽企业重金招聘Python工程师标准>>>

Spring3.1提供了新的属性管理API,而且功能非常强大且很完善,对于一些属性配置信息都应该使用新的API来管理。虽然现在Spring已经到4版本了,这篇文章来的晚点。

新的属性管理API

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

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

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

Profile:剖面,只有激活的剖面的组件/配置才会注册到Spring容器,类似于maven中profile

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

PropertySource

key-value对,API如下所示:

Java代码

  1. public String getName()  //属性源的名字
  2. public T getSource()        //属性源(比如来自Map,那就是一个Map对象)
  3. public boolean containsProperty(String name)  //是否包含某个属性
  4. public abstract Object getProperty(String name)   //得到属性名对应的属性值

非常类似于Map;用例如下:

Java代码

  1. @Test
  2. public void test() throws IOException {
  3. Map<String, Object> map = new HashMap<>();
  4. map.put("encoding", "gbk");
  5. PropertySource propertySource1 = new MapPropertySource("map", map);
  6. System.out.println(propertySource1.getProperty("encoding"));
  7. ResourcePropertySource propertySource2 = new ResourcePropertySource("resource", "classpath:resources.properties"); //name, location
  8. System.out.println(propertySource2.getProperty("encoding"));
  9. }

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

Java代码

  1. @Test
  2. public void test2() throws IOException {
  3. //省略propertySource1/propertySource2
  4. CompositePropertySource compositePropertySource = new CompositePropertySource("composite");
  5. compositePropertySource.addPropertySource(propertySource1);
  6. compositePropertySource.addPropertySource(propertySource2);
  7. System.out.println(compositePropertySource.getProperty("encoding"));
  8. }

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

另外还有一个PropertySources,从名字可以看出其包含多个PropertySource:

Java代码

  1. public interface PropertySources extends Iterable<PropertySource<?>> {
  2. boolean contains(String name); //是否包含某个name的PropertySource
  3. PropertySource<?> get(String name); //根据name找到PropertySource
  4. }

示例如下:

Java代码

  1. @Test
  2. public void test3() throws IOException {
  3. //省略propertySource1/propertySource2
  4. MutablePropertySources propertySources = new MutablePropertySources();
  5. propertySources.addFirst(propertySource1);
  6. propertySources.addLast(propertySource2);
  7. System.out.println(propertySources.get("resource").getProperty("encoding"));
  8. for(PropertySource propertySource : propertySources) {
  9. System.out.println(propertySource.getProperty("encoding"));
  10. }
  11. }

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

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

PropertyResolver

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

Java代码

  1. public interface PropertyResolver {
  2. //是否包含某个属性
  3. boolean containsProperty(String key);
  4. //获取属性值 如果找不到返回null
  5. String getProperty(String key);
  6. //获取属性值,如果找不到返回默认值
  7. String getProperty(String key, String defaultValue);
  8. //获取指定类型的属性值,找不到返回null
  9. <T> T getProperty(String key, Class<T> targetType);
  10. //获取指定类型的属性值,找不到返回默认值
  11. <T> T getProperty(String key, Class<T> targetType, T defaultValue);
  12. //获取属性值为某个Class类型,找不到返回null,如果类型不兼容将抛出ConversionException
  13. <T> Class<T> getPropertyAsClass(String key, Class<T> targetType);
  14. //获取属性值,找不到抛出异常IllegalStateException
  15. String getRequiredProperty(String key) throws IllegalStateException;
  16. //获取指定类型的属性值,找不到抛出异常IllegalStateException
  17. <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
  18. //替换文本中的占位符(${key})到属性值,找不到不解析
  19. String resolvePlaceholders(String text);
  20. //替换文本中的占位符(${key})到属性值,找不到抛出异常IllegalArgumentException
  21. String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
  22. }

从API上我们已经看出解析器的作用了,具体功能就不要罗嗦了。示例如下:

Java代码

  1. @Test
  2. public void test() throws Exception {
  3. //省略propertySources
  4. PropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources);
  5. System.out.println(propertyResolver.getProperty("encoding"));
  6. System.out.println(propertyResolver.getProperty("no", "default"));
  7. System.out.println(propertyResolver.resolvePlaceholders("must be encoding ${encoding}"));  //输出must be encoding gbk
  8. }

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

Environment

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

Java代码

  1. public interface Environment extends PropertyResolver {//继承PropertyResolver
  2. //得到当前明确激活的剖面
  3. String[] getActiveProfiles();
  4. //得到默认激活的剖面,而不是明确设置激活的
  5. String[] getDefaultProfiles();
  6. //是否接受某些剖面
  7. boolean acceptsProfiles(String... profiles);
  8. }

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

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

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

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

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

Java代码

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

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

在web环境中首先在web.xml中配置:

Java代码

  1. <context-param>
  2. <param-name>myConfig</param-name>
  3. <param-value>hello</param-value>
  4. </context-param>
  5. <servlet>
  6. <servlet-name>spring</servlet-name>
  7. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  8. <init-param>
  9. <param-name>contextConfigLocation</param-name>
  10. <param-value>classpath:spring-mvc.xml</param-value>
  11. </init-param>
  12. </servlet>

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

然后在程序中通过如下代码注入Environment:

Java代码

  1. @Autowired
  2. Environment env;

另外也可以直接使用ApplicationContext.getEnvironment()获取;接着就可以用如下代码获取配置:

Java代码

  1. System.out.println(env.getProperty("myConfig"));
  2. System.out.println(env.getProperty("contextConfigLocation"));

另外我们在运行应用时可以通过-D传入系统参数(System.getProperty()),如java -Ddata=123  com.sishuok.spring3.EnvironmentTest,那么我们可以通过environment.getProperty("data") 获取到。

如果我们拿到的上下文是ConfigurableApplicationContext类型,那么可以:ctx.getEnvironment().getPropertySources() ;然后通过PropertySources再添加自定义的PropertySource。

Profile

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

profile有两种:

默认的:通过“spring.profiles.default”属性获取,如果没有配置默认值是“default”

明确激活的:通过“spring.profiles.active”获取

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

API请参考Environment部分。设置profile属性,常见的有三种方式:

一、启动Java应用时,通过-D传入系统参数

Java代码

  1. -Dspring.profiles.active=dev

二、如果是web环境,可以通过上下文初始化参数设置

Java代码

  1. <context-param>
  2. <param-name>spring.profiles.active</param-name>
  3. <param-value>dev</param-value>
  4. </context-param>

三 、通过自定义添加PropertySource

Java代码

  1. Map<String, Object> map = new HashMap<String, Object>();
  2. map.put("spring.profiles.active", "dev");
  3. MapPropertySource propertySource = new MapPropertySource("map", map);
  4. env.getPropertySources().addFirst(propertySource);

四、直接设置Profile

Java代码

  1. env.setActiveProfiles("dev", "test");

以上方式都可以设置多个profile,多个之间通过如逗号/分号等分隔。

接着我们就可以通过如下API判断是否激活相应的Profile了:

Java代码

  1. if(env.acceptsProfiles("dev", "test"))) {
  2. //do something
  3. }

它们之间是或的关系;即找到一个即可;如果有人想不匹配某个profile执行某些事情,可以通过如"!dev"  即没有dev激活时返回true。

当然这种方式还不是太友好,还需要我们手工编程使用,稍候会介绍如何更好的使用它们。

<context:property-placeholder/>

${key}占位符属性替换器,配置如下:

Java代码

  1. <context:property-placeholder
  2. location="属性文件,多个之间逗号分隔"
  3. file-encoding="文件编码"
  4. ignore-resource-not-found="是否忽略找不到的属性文件"
  5. ignore-unresolvable="是否忽略解析不到的属性,如果不忽略,找不到将抛出异常"
  6. properties-ref="本地Properties配置"
  7. local-override="是否本地覆盖模式,即如果true,那么properties-ref的属性将覆盖location加载的属性,否则相反"
  8. system-properties-mode="系统属性模式,默认ENVIRONMENT(表示先找ENVIRONMENT,再找properties-ref/location的),NEVER:表示永远不用ENVIRONMENT的,OVERRIDE类似于ENVIRONMENT"
  9. order="顺序"
  10. />

location:表示属性文件位置,多个之间通过如逗号/分号等分隔;

file-encoding:文件编码;

ignore-resource-not-found:如果属性文件找不到,是否忽略,默认false,即不忽略,找不到将抛出异常

ignore-unresolvable:是否忽略解析不到的属性,如果不忽略,找不到将抛出异常

properties-ref:本地java.util.Properties配置

local-override:是否本地覆盖模式,即如果true,那么properties-ref的属性将覆盖location加载的属性

system-properties-mode:系统属性模式,ENVIRONMENT(默认),NEVER,OVERRIDE

ENVIRONMENT:将使用Spring 3.1提供的PropertySourcesPlaceholderConfigurer,其他情况使用Spring 3.1之前的PropertyPlaceholderConfigurer

如果是本地覆盖模式:那么查找顺序是:properties-ref、location、environment,否则正好反过来;

OVERRIDE: PropertyPlaceholderConfigurer使用,因为在spring 3.1之前版本是没有Enviroment的,所以OVERRIDE是spring 3.1之前版本的Environment

如果是本地覆盖模式:那么查找顺序是:properties-ref、location、System.getProperty(),System.getenv(),否则正好反过来;

NEVER:只查找properties-ref、location;

order:当配置多个<context:property-placeholder/>时的查找顺序,关于顺序问题请参考:http://www.iteye.com/topic/1131688

具体使用请参考如下文件中的如dataSource:

https://github.com/zhangkaitao/es/blob/master/web/src/main/resources/spring-config.xml

@PropertySource()

Spring 3.1提供的Java Config方式的注解,其属性会自动注册到相应的Environment;如:

Java代码

  1. @Configuration
  2. @PropertySource(value = "classpath:resources.properties", ignoreResourceNotFound = false)
  3. public class AppConfig {
  4. }

接着就可以使用env.getProperty("encoding")得到相应的属性值。

另外如果想进行Bean属性的占位符替换,需要注册PropertySourcesPlaceholderConfigurer:

Java代码

  1. @Bean
  2. public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
  3. return new PropertySourcesPlaceholderConfigurer();
  4. }

如上配置等价于XML中的<context:property-placeholder/>配置。

如果想导入多个,在Java8之前需要使用@PropertySources注册多个@PropertySource()。

此处要注意:

使用<context:property-placeholder/>不会自动把属性注册到Environment中,而@PropertySource()会;且在XML配置中并没有@PropertySource()等价的XML命名空间配置,如果需要,可以自己写一个。

占位符替换

使用Environment属性替换,如:

<context:property-placeholder location="classpath:${env}/resources.properties"/>

<context:component-scan base-package="com.sishuok.${package}"/>

<import resource="classpath:${env}/ctx.xml"/>

@PropertySource(value = "classpath:${env}/resources.properties")

@ComponentScan(basePackages = "com.sishuok.${package}")

@ImportResource(value = {"classpath:${env}/cfg.xml"})

@Value("${env}")

new ClassPathXmlApplicationContext("classpath:${env}/cfg.xml")

使用PropertySourcesPlaceholderConfigurer / PropertyPlaceholderConfigurer进性Bean属性替换,如:

Java代码

  1. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  2. <!-- 基本属性 url、user、password -->
  3. <property name="url" value="${connection.url}"/>
  4. <property name="username" value="${connection.username}"/>
  5. <property name="password" value="${connection.password}"/>
  6. </bean>

SpEL表达式:

请参考【第五章】Spring表达式语言 之 5.4在Bean定义中使用EL—跟我学spring3

通过如上方式可以实现不同的环境有不同的属性配置,但是如果我们想不同的环境加载不同的Bean呢,比如测试机/正式机环境可能使用远程方式访问某些API,而开发机环境使用本地方式进行开发,提高开发速度,这就需要profile了。

<beans  profile="">

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

Java代码

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  4. <beans profile="dev">
  5. <bean id="dataSource" class="本地DataSource">
  6. </bean>
  7. </beans>
  8. <beans profile="test">
  9. <bean id="dataSource" class="测试环境DataSource">
  10. </bean>
  11. </beans>
  12. </beans>

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

@Profile()

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

Java代码

  1. @Profile("dev")
  2. @Configuration
  3. @PropertySource(value = "classpath:resources.properties", ignoreResourceNotFound = false)
  4. public class AppConfig {
  5. @Bean
  6. public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
  7. return new PropertySourcesPlaceholderConfigurer();
  8. }
  9. }

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

@ActiveProfiles()

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

Java代码

  1. @ActiveProfiles("test")
  2. @RunWith(SpringJUnit4ClassRunner.class)
  3. @ContextConfiguration(classes = GenericConfig.class)
  4. public class GenricInjectTest {
  5. ……
  6. }

通过这种方式,我们就激活了test profile。

到此整个Spring的属性管理API就介绍完了,对于属性管理,核心是Environment,所以以后请使用Environment来进行属性管理吧。

转载于:https://my.oschina.net/xiaominmin/blog/1603420

Spring3.1新属性管理API:PropertySource、Environment、Profile相关推荐

  1. mysql8.0创建属性,MySQL 8.0新特性 — 管理端口的使用简介

    前言 下面这个报错,相信大多数童鞋都遇见过:那么碰到这个问题,我们应该怎么办呢?在MySQL 5.7及之前版本,出现"too many connection"报错,超级用户root ...

  2. multipartfile前端怎么传_前端那些事如何更好管理 Api 接口

    ❝ 前沿:自从前端和后端分家之后,前后端接口对接就成为了家常,"谁"也离不开谁,而对接接口的过程就离不开接口文档,比较主流就是Swagger(强大的API文档工具),当然今天它不是 ...

  3. Android 11 新特性和API兼容

    这个文档主要产品层面,新的特性..兄弟blog,Android11的Api变化和迁移变化. 1,新特性 1.1,设备控件 Android 11 包含一个新的 ControlsProviderServi ...

  4. jenkins api使用_使用管理API和Jenkins作为IBM App Connect Professional部署自动化的持续集成引擎

    在本教程中,您将了解使用IBM App Connect Management API,SoapUI客户端和Jenkins在IBM App Connect(以前称为WebSphere®CastIron® ...

  5. Atitit.研发管理---api版本号策略与版本控制

    Atitit.研发管理---api版本号策略与版本控制 1. 1.2.1版本概述1 2. 3主版本号策略2 3. 1PATCH版本策略2 3.1. 1.2.2.1次版本号策略2 表3-1 APR中支持 ...

  6. Asp.net用户管理API的应用(上)

    Asp.net官方标准控件实现用户的管理,虽然简单,但控件封装性很强,开发人员不能明白做了什么样的调用,还用别一方面,标准控件的使用,很大程度上限制了程序的可变性.如果自开发一整套用户管理系统,可行, ...

  7. kie-api_7.0上的新KIE持久性API

    kie-api 这篇文章介绍了即将到来的Drools和jBPM持久性API. 创建持久性api(不绑定到JPA)的动机是因为Drools和jBPM中的持久性直到7.0.0发行版才允许将替代性持久性机制 ...

  8. 7.0上的新KIE持久性API

    这篇文章介绍了即将到来的Drools和jBPM持久性api. 创建持久性api(不绑定到JPA)的动机是因为Drools和jBPM中的持久性直到7.0.0发行版才允许将替代性持久性机制与JPA完全集成 ...

  9. Java SE 8新功能介绍:使用新的DateTime API计算时间跨度

    使用Java SE 8新的DateTime API JSR 310-可以实现更清晰,可读且功能强大的编码. Java SE 8,JSR 310 在上一篇文章" 使用Streams API处理 ...

最新文章

  1. 万物智联时代——2018年AIOT产业蓝皮书正式发布
  2. html5 css3中的一些笔记
  3. 学习Java编程到底是为了什么,该具备什么样的能力
  4. 用java做出两行三列的表格_Java中,使用HSSFSheet创建excel模板如何创建一列两行的数据?...
  5. 商用计算机低温工作,突破量子计算机瓶颈!超低温芯片能在接近绝对零度的温度下工作...
  6. php开发工程师考试试卷,腾讯PHP开发工程师面试试卷
  7. TikTok:将禁止加密货币广告投放
  8. Maven打jar包的三种方式
  9. 菜鸟从零学习数据库(三)——存储过程
  10. cad汉仪长仿宋体_长仿宋体字体下载 cad工程机械绘图工程制图国标字体下载
  11. MATLAB画演化博弈图,演化博弈matlab程序与作图
  12. adobe reader XI 打开后闪退(或过几秒后自动退出)【解决方案】
  13. htcd816+android密码,HTC816怎么解锁?HTC Desire 816刷机解锁教程图解
  14. 宇枫资本投资过程中要注意这些习惯
  15. wifi信号正常,电脑插入网线之后,却显示“未识别网络”,
  16. 大数据产品开发流程规范_大数据系统开发步骤流程是怎样的
  17. JS字符(字母)与ASCII码转换
  18. 数据库 MySQL 中 DQL 数据库查询语言(特别重要)
  19. win10开始菜单打不开_windows10系统电脑开始菜单无法打开的解决教程
  20. c语言大作业打印课程表,课程表(c语言)

热门文章

  1. PHP 通过fsockopen函数获取远程网页源码
  2. servlet设置session追踪模式
  3. 华为交换机网络管理相关配置问题(1)
  4. ExtJs4–表格--Grid
  5. 生产性服务业的源起及内涵(制造服务业的前世今生之一)
  6. 毒霸能清除的大小流氓清单(部分)
  7. 学web前端的第一天
  8. 爬取校园新闻首页的新闻
  9. css 命名规范 BEM
  10. 具体解释Hibernate中的事务