《Spring实战》-第三章:Bean的高级装配(5)-运行时值注入

慢慢来比较快,虚心学技术

当我们讨论依赖注入的时候,多数注意的都是将一个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单独作为一个列表返回)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章