Spring提供了2种方式在运行时注入值:

  1. 属性占位符(Property placeholder)
  2. Spring表达式语言(SpEL)

1. 属性占位符

1.1 注入外部的值

1.1.1 使用Environment

一般情况下,我们会将一些值放到配置文件中,等程序运行时再把值注入到一些字段上。

假如,我们有一个test.properties配置文件,内容如下:

book.author=wangyunfei
book.name=spring boot
author.age=30

现在我们希望在程序运行时,把这个值分别赋值给字段bookAuthor和bookName,那么该如何实现呢?

首先,新建配置类ExpressiveConfig如下:

package chapter03.el;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment; @Configuration
@ComponentScan
@PropertySource("classpath:chapter03/el/test.properties")
public class ExpressiveConfig {
@Autowired
private Environment environment; public void outputResource() {
System.out.println("book.name:" + environment.getProperty("book.name"));
System.out.println("book.author:" + environment.getProperty("book.author"));
}
}

这里我们使用@PropertySource注解引用了test.properties配置文件,这个文件的位置位于chapter03.el包下。

这个属性文件会加载到Spring的Environment中,然后我们就可以调用getProperty()方法获取到属性值。

新建Main类,在其main()方法中添加如下测试代码:

package chapter03.el;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExpressiveConfig.class); ExpressiveConfig expressiveConfig = context.getBean(ExpressiveConfig.class); expressiveConfig.outputResource(); context.close();
}
}

运行代码,发现抛出java.io.FileNotFoundException异常,如下所示:

从报错信息可以看出, 这是提示找不到chapter03/el/test.properties这个文件,这是为什么呢?

带着这个疑问,我们看下target目录下编译后的代码,如下所示:

从图中可以看出,我们新建的test.properties和test.txt文件并没有被编译到target目录下,所以才会抛出异常。

这是因为,我们新建文件的位置放在chapter03.el包下,而IDEA默认是不会把这些文件自动复制到target目录下的,但我们可以在pom.xml中添加如下配置来解决该问题:

<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.txt</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>

这里我们指定了txt和properties文件,如果需要,可以继续添加<include>标签指定xml等文件。

再次运行测试代码,输出日志如下所示:

book.name:spring boot

book.author:wangyunfei

此时target目录下已经包含了我们新建的2个文件:

如果指定的属性值不存在,getProperty()会返回null,如下所示:

String workCity = environment.getProperty("author.workcity");
System.out.println("author.workcity:" + workCity);

输出结果:

author.workcity:null

getProperty()还提供了1个重载,当指定的属性值不存在时,可以指定默认值:

String workCity = environment.getProperty("author.workcity", "上海");
System.out.println("author.workcity:" + workCity);

输出结果:

author.workcity:上海

如果希望属性值必须存在,可以使用getRequiredProperty()方法,当属性值不存在时,会抛出java.lang.IllegalStateException异常:

String workCity = environment.getRequiredProperty("author.workcity");
System.out.println("author.workcity:" + workCity);

getProperty()还提供了1个重载,可以指定返回值的类型,比如我们想返回Integer类型:

Integer authorAge = environment.getProperty("author.age", Integer.class);
System.out.println("author.age:" + authorAge);

输出结果:

author.age:30

getProperty()还提供了1个重载,当指定的属性值不存在时,不仅可以指定默认值,还可以指定返回值类型:

boolean isMan = environment.getProperty("author.isMan", Boolean.class, true);
System.out.println("author.isMan:" + isMan);

输出结果:

author.isMan:true

1.1.2 使用属性占位符

除了使用Environment获取外部的属性值,我们还可以使用属性占位符来获取。

在Spring装配中,占位符的形式为使用“${......}”包装的属性名称。

新建Book类如下:

package chapter03.el;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; @Component
public class Book {
@Value("${book.name}")
private String bookName; @Value("${book.author}")
private String bookAuthor; public String getBookName() {
return bookName;
} public void setBookName(String bookName) {
this.bookName = bookName;
} public String getBookAuthor() {
return bookAuthor;
} public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
}

可以发现,我们在字段上添加了@Value注解,参数传的值就是属性占位符,用来获取属性文件中指定的属性值。

然后,在ExpressiveConfig配置类中添加如下代码:

@Autowired
private Book book; public void outputResource() {
System.out.println("book.name:" + book.getBookName());
System.out.println("book.author:" + book.getBookAuthor());
}

输出结果:

book.name:spring boot

book.author:wangyunfei

2. Spring表达式语言

Spring表达式语言(Spring Expression Language,SpEL)是一种非常灵活的表达式语言,能够以一种强大和简洁的方式将值装配到bean属性或者构造器参数中,在这个过程中所使用的的表达式会在运行时计算值。

SpEL表达式要放到“#{......}”之中,而之前讲到的属性占位符是放到“${......}”之中。

接下来,我们分场景来看下Spring表达式语言的使用方法。

2.1 引用系统属性值

在ExpressiveConfig中添加如下代码:

@Value("#{systemProperties['os.name']}")
private String osName; public void outputResource() {
System.out.println("os.name:" + osName);
}

输出结果:

os.name:Windows 7

2.2 引用bean的属性和方法

首先,新建一个类DemoService如下所示:

package chapter03.el;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; @Service
public class DemoService {
@Value("DemoService类的another属性")
private String another; public String getAnother() {
return another;
} public void setAnother(String another) {
this.another = another;
}
}

然后,在ExpressiveConfig中添加如下代码:

@Value("#{demoService.another}")
private String fromAnother; public void outputResource() {
System.out.println("demoService.another:" + fromAnother);
}

表达式中的demoService为DemoService bean的ID,another是它的属性。

输出结果:

demoService.another:DemoService类的another属性

表达式也可以修改为调用bean的方法:

@Value("#{demoService.getAnother()}")
private String fromAnother;

输出结果不变,只是从调用属性变成了调用方法。

调用完方法,可以对方法的返回值继续调用其它方法,比如toUpperCase():

@Value("#{demoService.getAnother()?.toUpperCase()}")
private String fromAnother;

之所以使用"?."运算符,是为了避免当demoService.getAnother()返回null时,代码出现NullPointerException。

此时的输出结果为:

demoService.another:DEMOSERVICE类的ANOTHER属性

2.3 在表达式中使用类型

使用表达式生成1个随机数:

@Value("#{T(java.lang.Math).random()}")
private double randomNumber; public void outputResource() {
System.out.println("randomNumber:" + randomNumber);
}

这里我们使用T()引用了java.lang.Math类,然后调用了它的静态方法random()。

输出结果:

randomNumber:0.6801944394506442

2.4 使用运算符

上面的例子中,生成随机数后,我们还可以使用乘法运算符,如下所示:

@Value("#{T(java.lang.Math).random() * 100.0}")
private double randomNumber;

我们也可以在表达式中使用“+”运算符拼接字符串,如下所示:

@Value("#{book.getBookName() + ' write by ' + book.getBookAuthor()}")
private String bookDescr; public void outputResource() {
System.out.println("bookDescr:" + bookDescr);
}

其中book为Book bean的ID,输出结果如下所示:

bookDescr:spring boot write by wangyunfei

也可以在表达式中使用三元运算符:

@Value("#{systemProperties['os.name'] == 'Windows 7'?'Windows':'Linux'}")
private String osType; public void outputResource() {
System.out.println("osType:" + osType);
}

因为我的电脑系统是Windows 7,所以输出结果如下所示:

osType:Windows

SpEL还支持很多的运算符,这里只是列举了几个常用的例子,有兴趣的同学可以自己深入研究下。

3. 源码及参考

源码地址:https://github.com/zwwhnly/spring-action.git,欢迎下载。

Craig Walls 《Spring实战(第4版)》

汪云飞《Java EE开发的颠覆者:Spring Boot实战》

IDEA maven项目src源代码下的资源文件不自动复制到classes文件夹的解决方法

原创不易,如果觉得文章能学到东西的话,欢迎点个赞、评个论、关个注,这是我坚持写作的最大动力。

如果有兴趣,欢迎添加我的微信:zwwhnly,等你来聊技术、职场、工作等话题(PS:我是一名奋斗在上海的程序员)。

Spring入门(九):运行时值注入的更多相关文章

  1. Spring入门(4)-注入Bean属性

    Spring入门(4)-注入Bean属性 本文介绍如何注入Bean属性,包括简单属性.引用.内部Bean.注入集合等. 0. 目录 注入简单值 注入引用 注入内部Bean 装配集合 装配空值 使用命名 ...

  2. spring in action小结3 运行时值注入

    讨论依赖注入的时候,通常讨论的是一个bean引用注入到另一个bean的属性或者构造器参数中.bean装配的另一个方面是将值注入到bean的属性或者构造器参数中.避免硬编码的方式就是运行时确定值. sp ...

  3. Spring实战(八)bean装配的运行时值注入——属性占位符和SpEL

    前面涉及到依赖注入,我们一般哦都是将一个bean引用注入到另一个bean 的属性or构造器参数or Setter参数,即将为一个对象与另一个对象进行关联. bean装配的另一个方面是指将一个值注入到b ...

  4. Spring入门之setter DI注入

    1.新建Java项目导入依赖jar包,参考前一章 2.以不同文件格式输出为例 3.定义接口IOutputGenerator.java package com.spring.output; public ...

  5. 【Spring Framework】Spring 入门教程(一)控制反转和依赖注入

    参考资料 Spring 教程 说在前面 什么样的架构,我们认为是一个优秀的架构? 判断准则:可维护性好,可扩展性好,性能. 什么叫可扩展性好? 答:在不断添加新的代码的同时,可以不修改原有代码,即符合 ...

  6. Spring入门(2)-通过构造器注入Bean

    Spring入门(2)-通过构造器注入Bean 前一篇文章将了最基本的spring例子,这篇文章中,介绍一下带有参数的构造函数和通过构造器注入对象引用. 0. 目录 带有参数的构造函数 通过构造器注入 ...

  7. SPRING IN ACTION 第4版笔记-第三章ADVANCING WIRING-007-给BEAN运行时注入值placeholder、@Value

    一.用placeholder给bean运行时注入值的步骤 Spring取得placeholder的值是用${...} 1.声明placeholder bean (1)java方式 In order t ...

  8. SPRING IN ACTION 第4版笔记-第三章ADVANCING WIRING-006-给bean运行时注入值(Environment,Property文件)

    一. 直观的给bean注入值如下: @Bean public CompactDisc sgtPeppers() { return new BlankDisc( "Sgt. Pepper's ...

  9. Spring学习笔记之 Spring IOC容器(二) 之注入参数值,自动组件扫描方式,控制Bean实例化方式,使用注解方式

     本节主要内容:    1. 给MessageBean注入参数值    2. 测试Spring自动组件扫描方式    3. 如何控制ExampleBean实例化方式    4. 使用注解方式重构Jdb ...

随机推荐

  1. DAX 第一篇:数据模型

    DAX是一种专门用于计算数据模型的业务公式的语言,本文以Power BI的关系来学习数据模型. 一,理解数据模型 数据模型是由一组表和关系构成的结构,表和表之间由关系链接,如下图所示的产品数据模型: ...

  2. 用MATLB仿真一个单闭环控制量,同时还存在两个开环控制变量的阶跃响应曲线。(自动控制方法是PID中的P控制。通过查表法直接给开环参数稳态最佳的大小)

    实际项目背景:甘肃省,航天510所的LIPS100电推力器.一共有三个控制变量,开环控制变量是:Ia(阳极电流).mmrf(阳极主流率) 这个阳极主流率是阀门变量,不能够突变,模拟用(大学一年级课,电 ...

  3. [leetcode] 238. Product of Array Except Self (medium)

    原题 思路: 注意时间复杂度,分别乘积左右两边,可达到O(n) class Solution { public: vector<int> productExceptSelf(vector& ...

  4. fiddle知识点六、如何使用fiddle进行模拟弱网

    为什么要模拟弱网 随着互联网的快速发展,越来越多的应用核心功能需要网络进行实现.同一应用在2G.3G.4G和WiFi的不停网络下,响应各有不同.但是因为现在的网络普遍为4G网络,为了保证应用在不同的网 ...

  5. 【MySQL】(三)文件

    本篇文章分析构成MySQL数据库和InnoDB存储引擎表的各种累类型文件.这些文件有以下这些. 参数文件:告诉MySQL实例启动时在哪里可以找到数据库文件,并且指定某些初始化参数,这些参数定义了某种内 ...

  6. win8上安装sql server (2008 and 2008 R2)存在兼容性问题怎么解决

     安装sql2008R2之前,需要先下载"AccessDatabaseEngine"组件并安装(百度上搜索下载就可以了),打补丁重启,再安装sql2008,安装时候提示兼容性问 ...

  7. d3.js实现柱形图,饼图以及折现图

    饼图 var width = 500; var height = 500; //处理数据 var dataset = [ 30 , 10 , 43 , 55 , 13 ]; var pie = d3. ...

  8. spring 事务隔离级别导致的bug

    事情是这样的,ios进货单有一个数量加一手,减一手的功能,每加减一次就会异步调用后台的接口,后台判断sku如果不存在就插入,存在就更新.   问题描述: 当ios发了多次请求后, 在第二次请求的时候, ...

  9. HTML&CSS兼容性总结

    对目前所遇见的兼容性笔记进行整理分类: 不兼容浏览器 问题概要 问题描述 解决方法 IE6,IE7  3px 并列一行的元素左侧第一个元素没浮动,第二个元素左浮动,则两个元素之间会多3像素空隙 并在一 ...

  10. 使用vsftp与shell实现对进程与服务状态的监控

    先说一下需求吧,公司开发了一款新的产品,新产品嘛,有着不得不出问题的理由,四个云机房,总共三百余台机器,需要实时的监控进程状态,虽然有zabbix来实现,但领导需求是脚本和zabbix一起做,zabb ...