《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单独作为一个列表返回)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容