慢慢来比较快,虚心学技术
当我们讨论依赖注入的时候,多数注意的都是将一个bena作为属性或构造器参数注入到另一个bean中。但是有时候我们也需要关注如何将值注入到方法参数或者属性中去,在前面的文章中,我们所了解的注入都是在系统初始化的时候就已经写死的值,可是有时候我们也需要在运行时才确定需要注入的值,Spring提供了两种运行时注入的方式:
- 属性占位符( Property placeholder )。
- Spring 表达式语言( SpEL )
一、属性占位符
在Spring中,最简单直接的外部值注入方式是属性占位符,通过读取外部属性文件的值并注入到bean中。此处涉及@PropertySource注解和之前提到的Enviroment对象
- @PropertySource:将目标路径的属性文件引入当前环境
- Enviroment对象:封装了当前环境的属性,用于读取环境中的属性
如,我们现在创建一个属性文件application.properties和一个基本类BeseBean,一个JavaConfig配置类,并将application.properties对应的属性值传入BaseBean中
#application.properties
properties.name=Properties name
//BaseBean.class
public class BaseBean {
private String name="BaseBean";
}
//BaseConfig.class
@Configuration
@PropertySource("classpath:application.properties")
public class BeanConfig {
@Autowired
Environment environment;
//定义bean并将环境中key为properties.name注入到BaseBean的构造函数参数中
@Bean
public BaseBean baseBean(){
return new BaseBean(environment.getProperty("properties.name"));
}
}
//测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {BeanConfig.class})
public class AppBaseConfigTest {
@Autowired
private BaseBean baseBean;
@Test
public void getBean(){
System.out.println(baseBean.getName());
}
}
//测试结果:并不是默认的“BaseBean”,而是properties文件中定义好的Properties name
Properties name
上述代码演示了通过@PropertySource注解和Enviroment对象实现外部值注入到bean。实际上,Enviroment提供了几个获取环境属性值的实现方法:
- String getProperty(String key); //以key获取对应值(String类型)
- String getProperty(String key, String defaultValue); //以key获取对应值,当不存在对应的key或对应value为null时,使用defaultValue(String类型)
- <T> T getProperty(String key, Class<T> type); //以key获取对应值(并强转为type类型)
- <T> T getProperty(String key, Class<T> type, T defaultValue); //以key获取对应值(并强转为type类型),当不存在对应的key或对应value为null时,使用defaultValue(type类型)
如:假设BaseBean内有属性age类型为Integer,price属性类型为double,系统注入如下:
#application.properties
properties.name=Properties name
properties.age=50
properties.price=1.5
//定义bean并将环境中key为properties.name注入到BaseBean的构造函数参数中
@Bean
public BaseBean baseBean(){
BaseBean baseBean = new BaseBean(environment.getProperty("properties.name"));
baseBean.setAge(enviroment.getProperty("properties.age",Integer.class,new Integer(10)));
baseBean.setPrice(enviroment.getProperty("properties.price",Double.class,new Double(10.5)))
}
二、Spring表达式(SpEL)
SpEL( Spring Expression Language,Spring表达式语言),是一种灵活的表达式,它能够以一种强大和简洁的方式将值装配到 bean 属性和构造器参数中,在这个过程中所使用的表达式会在运行时计算得到值。
SpEL具备如下特性:
-
使用 bean 的 ID 来引用 bean ;
-
调用方法和访问对象的属性;
-
对值进行算术、关系和逻辑运算;
-
正则表达式匹配;
-
集合操作
利用这些特性可以为我们执行很多强大的功能, SpEL 表达式要放到 “#{ ... }” 之中,使用在@Value()注解里面
Ⅰ、使用字面量值
SpEL可以注入字面量值,使用方式:#{1}、#{'Test'},#{1.09},#{flase}等
如上述例子中的BaseBean,我们通过SqEL为其注入一个name,注意:一定要去除默认构造函数,否则自动扫描不会执行目标构造函数
public class BaseBean {
private String name="BaseBean";
public BaseBean(@Value("#{'SqEL Name'}")String name){
this.name = name;
}
}
Ⅱ、引用bean属性和方法
SpEL还可以根据bean的ID获取到bean并将该bean的,该bean的属性或执行该bean方法的返回值注入到参数中
如,将baseBean中的name属性传入sonBean的name属性中
@Component
public class SonBean {
private String sonName="sonName";
public SonBean(@Value("#{baseBean.name}") String sonName){
this.sonName = sonName;
}
}
执行baseBean中的getName方法将返回值注入到SonBean的构造方法中:
@Component
public class SonBean {
private String sonName="sonName";
public SonBean(@Value("#{baseBean.getName())}") String sonName){
this.sonName = sonName;
}
}
假设SonBean中有BaseBean属性,将baseBean示例注入到SonBean的构造方法中:
@Component
public class SonBean {
private String sonName="sonName";
private BaseBean baseBean=null;
public SonBean(@Value("#{baseBean}") BaseBean baseBean){
this.baseBean = baseBean;
}
}
注:如果一个类中存在多个构造函数,那么使用自动化配置方式装配Bean的时候会报装配失败异常
Ⅲ、在表达式中使用类型
有时候我们需要在SpEL中使用一些类作用域的方法和属性,需要使用T()关键子运算符,其运算结果是一个Class,关键作用在于我们能够利用它访问静态方法和属性。
如:获取Math类中的PI和随机数方法
//获取PI值
#{T(java.lang.Math).PI}
//获取随机数
#{T(java.lang.Math).random()}
Ⅳ、运算符
在SpEL中我们可以像正常java语法中一样使用运算符,其中比较特殊也比较常用的是三目表达式 ?:
//判断如果baseBean的age属性值大于50,则返回该age,否则返回60
#{baseBean.age>50?baseBean.age:60}
//判断baseBean的age是否null,如果是,则返回60
#{baseBean.age?:60}
Ⅴ、正则表达式(matches)
使用SpEL进行正则匹配依赖于matches运算符,该运算符返回一个boolean值,语法类似java
//baseBean的name是否全英文
#{baseBean.name matches '[a-zA-Z]*'}
Ⅵ、集合计算
使用SpEL可以对集合做很多方便的操作,主要依赖于"[]"运算符实现,如:
假设现有一个BaseBean列表
①获取集合某个元素的name属性:#{beanList[4].name}
②获取字符串中的某个下标的字符:#{'The Test'[3]}
③获取集合中符合条件的元素(查询运算符:.?[]):#{beanList.?[name eq 'Mr D']}(查找name等于”Mr D“的bean)
④获取集合中共符合条件的第一项元素(查询运算符:.^[]):#{beanList.^[name eq 'Mr D']}(查找name等于”Mr D“的第一个bean)
⑤获取集合中共符合条件的最后一项元素(查询运算符:.$[]):#{beanList.$[name eq 'Mr D']}(查找name等于”Mr D“的最后一个bean)
⑥将集合中的某个属性投影到另一个集合中(投影运算符:.![]):#{beanList.![name]}(将beanList中元素的name单独作为一个列表返回)