Bean定义配置文件
Bean定义配置文件在核心容器中提供了一种机制,该机制允许在不同环境中注册不同的Bean。 “环境”一词对不同的用户可能具有不同的含义,并且此功能可以帮助解决许多用例,包括
-
在开发中针对内存中的数据源进行工作,而不是在进行QA或生产时从JNDI查找相同的数据源。
-
仅在将应用程序部署到性能环境中时注册监视基础结构。
-
为客户A和客户B部署注册bean的自定义实现。
使用@Profile
@Profile批注可让您指示一个或多个指定的配置文件处于活动状态时有资格注册的组件
也就是为不同的@Configuration类标注环境,当设定的环境与规定的相同的@Configuration类才会被启用,该类下的bean才会生效
/** 配置生产环境调用类 **/
@service("productRpc")
@profile("prop")
public class ProductRpcImpl implements ProductRpc
public String productBaseInfo(Long sku){
return productResource.queryBaseInfo(Long sku);
}
}
/** 配置生产环境调用类 **/
@service("productRpc")
@profile("dev")
public class MockProductRpcImpl implements ProductRpc
public String productBaseInfo(Long sku){
return “iphone7”;
}
}
/** 调用类 **/
public class Demo(){
@Resource(name="productRpc")
private ProductRpc productRpc;
public void demo(){
String skuInfo = productRpc.productBaseInfo(123123L);
logger.info(skuInfo);
}
}
这样就完成了基于注解的profile配置。当配置为生产环境的时候会正常调用接口,当为开发环境的时候回调用mock接口。
xml配置profile
<!-- 开发环境 -->
<beans profile="dev">
<bean id="beanname" class="com.pz.demo.ProductRPC"/>
</beans>
<!-- 生产环境 -->
<beans profile="dev">
<bean id="beanname" class="com.pz.demo.MockProductRPC"/>
</beans>
激活profiles
spring在确定那个profile处于激活状态的时,需要依赖两个独立的属性:spring.profiles.active和spring.profile.default。如果设置了spring.profiles.actives属性,那么它的值就会用来确定那个profile是激活的。如果没有设置spring.profiles.active属性的话,那spring将会查找spring.profiles.default的值。
运行时值注入
依赖注入通常是将一个bean引用注入到另一个bean的属性或构造器参数中,通常讲的是将一个对象与另一个对象进行关联。
但是bean装配的另一个方面是将一个值注入到bean的属性或者构造器参数中。
利用硬编码可以实现一些需求,但是有时更希望这些值在运行时再确定。为此,Spring提供了两种方式:
- 属性占位符
- Spring表达式语言
注入外部的值
在Spring中,处理外部值的最简单方式就是声明属性源并通过Spring的Environment来检索属性。
@PropertySource要引用一个properties文件,该文件的大致内容为:
disc.title=test
disc.artist=Jay
这个属性文件会加载到Spring的Environment中,稍后可以用getProperty()来实现的。
深入学习Spring的Environment
getProperty()方法有四个重载的变形形式。
String getProperty(String key)
String getProperty(String key,String defaultValue)
T getProperty(String key,Class<T> type)
T getProperty(String key,Class<T> type,T defaultValue)
前两种形式的getProperty()方法都会返回String类型的值,而且第二个方法可以在指定属性不存在时,会使用一个默认值。
而后面两种方法不会简化所有的值都视为String类型。如果你想要获取的值代表的含义是连接池中所维持的连接数量,需要将String转化为Integer类型。
int connectionCount = env.getProperty("db.connection.count",Integer.class,30);
getRequiredProperty()要求获取的属性必须要有定义,否则会抛出IllegalStateException异常。
containsProperty()检查某一个属性是否存在。
getPropertyAsClass()可以将属性解析为类。
class<CompactDisc> cdClass = env.getPropertyAsClass("disc.class",CompactDisc.class)
关于profile的一些方法:
- String[] getActiveProfiles():返回激活profile名称的数组;
- String[] getDefaultProfiles():返回默认profile名称的数组;
- boolean acceptsProfiles(String … profiles):如果environment支持给定的profile,就返回true。在bean创建之前,使用acceptsProfiles()方法来确保给定bean所需的profile处于激活状态。
解析属性占位符
Spring一直支持将属性定义到外部的属性文件中,并使用占位符值将其插入到Spring bean中。在Spring装配中,占位符的形式为使用“${…}” 包装属性名称。
<bean id="sgtPeppers"
class="soundsystem.BlankDisc"
c:_title="${disc.title}"
c:_artist="${disc.artist}" />
title和artist参数所给定的值都是从一个属性中解析得到的。以这种方式,XML配置没有使用任何硬编码设置,值都是从配置文件以外解析得到的。
如果我们依赖于组件扫描和自动装配来创建和初始化应用组件的话,那么就没有指定占位符的配置文件或类了。这时,我们可以使用@Value注解
public BlankDisc(
@value("${disc.title}")String title,
@value("${disc.artist}") String artist){
this.title = title;
this.artist = artist;
}
要想使用占位符,必须要配置一个PropertyPlaceholderConfigurer bean或PropertySourcesPlaceholderConfigurer bean。从Spring 3.1开始,推荐使用后者,以为它能够基于Spring Environment及其属性源来解析占位符。
在Java中配置PropertySourcesholderConfigurer
@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
在XML中配置
Spring Context命名空间中的元素会为你生成PropertySourcesPlaceholderConfigurer bean。
使用Spring表达式语言进行装配
Spring表达式语言(SpEL)能够以一种强大和简洁的方式将值装配到bean属性和构造器参数中,在这个过程中所使用的表达式会在运行时计算得到的值。
特性:
- 使用bean的ID来引用bean;
- 调用方法和访问对象的属性;
- 对值进行算术、关系和逻辑运算;
- 正则表达式匹配;
- 集合操作。
SpEL样例
SpEL表达式要放到“#{ … }”中。
#{1}数字常量1
#{T(System).currentTimeMillis()}计算表达式的那一刻当前时间的毫秒数。T()表达式会将java.lang.System视为Java中对应的类型,因此可以调用其static修饰的currentTimeMillis()方法。
#{A.B}计算得到ID为A的bean的B属性
#{systemProperties[‘disc.title’]}通过systemProperties对象引用系统属性
在bean装配时使用这些表达式
使用组件扫描的话,在注入属性和构造器参数时,可使用@Value注解,与属性占位符很类似。
@Bean
public BlankDisc(
@value("#{systemProperties['disc.title']}")String title,
@value("#{systemProperties['disc.artist']}") String artist){
this.title = title;
this.artist = artist;
}
XML
可以将SpEL表达式传入或的value属性中,或者将其作为p-命名空间或c-命名空间条目的值。
表示字面值
SpEL不仅可以表示整数,还可以表示浮点数、String值以及Boolean值。
#{3.1415926}
#{9.8E4}科学计数法,结果为98700
#{‘Hello World’}
#{false}
引用bean、属性和方法
通过ID引用其他bean。
一个bean ID为sgtPeppers
#{sgtPeppers}
#{sgtPeppers.artist}
除了调用bean及其属性,还可以调用bean上的方法。对于被调用方法的返回值,我们同样可以调用它的方法。为了避免出现NullPointerException,可以使用类型安全的运算符,即返回值为空的话,不会调用后面的方法:
#{artistSelector.selectArtist()?.toUpperCase()}
在表达式中使用类型
通过T()运算符方法类作用的方法和常量。如果要使用Java的Math类,只需这样使用运算符:T(java.lang.Math)。我们甚至可以把它装配到一个Class类型的bean属性,但是T()的真正的价值在于它能够访问目标类型的静态方法和常量。
SpEL运算符
SpEL的运算符可以用在SpEL表达式上,运算符如下所示:
运算符类型 运算符
算术运算 +、-、*、/、%、^
比较运算 <、>、==、<=、>=、lt、gt、eq、le、ge、
逻辑运算 and、or、not、|
条件运算 ?: (ternary)、?: (Elvis)
正则表达式 matches
- 计算圆的面积 #{T(java.lang.Math).PI * circle.radius ^ 2}
当使用String类型时,’+’运算执行的是连接操作。
比较两个两个数字是否相等,可以使用双等号运算符(==)或文本型的eq运算符。计算结果是个Boolean值,相等为true,否则为false。
SpEL中的三元运算符与Java中的三元运算符非常相似。#{scoreboard.score > 1000 ? “winner” : “loser”}如果scoreboard.score > 1000 的话,计算结果为String类型的”winner”,否则结果为”loser”。同时,三元运算符还可以检查null值,并使用一个默认值来代替null。#{disc.title ?: ‘DJ’}即是判断disc.title的值是不是null,如果时null的话,结果就是”DJ”。
计算正则表达式
SpEL通过matches运算符支持表达式中的模式匹配。matches运算符对String类型的文本应用正则表达式,如果相匹配,返回true;否则返回false。
计算集合
引用列表中的一个元素:#{jukebox.songs[4].title}计算songs集合中第五个元素的title属性,该集合来源于ID为jukebox bean。也可以从集合中随机选取一首歌:
#{jukebox.songs[T(java.lang.Math).random()*jukebox.songs.size()].title}
1
可以利用”[]”从String中获取一个字符:#{‘This’[3]}引用该String中的第四个字符,也就是”s”。
查询运算符(.?[ ]):对集合进行过滤,得到集合的一个子集。”.^[ ]”和”.$[ ]”分别用来在集合中查询第一个匹配项和最后一个匹配项。#{jukebox.songs.?[artist eq ‘Jay’]}即查询得到Jay的所有歌曲。选择运算符在它的方括号内接受另一个表达式,迭代列表时,每一个条目都计算这个表达式,如果表达式为true将该条目放到新集合中,否则不会放到新集合中。
投影运算符(.![ ]):从集合的每个成员中选择特定的属性放到另外一个集合。#{jukebox.songs.?[artist eq ‘Jay’].![title]}即用来获取Jay所有歌曲的名称列表。