@Value 注解可以用来将外部的值动态注入到 Bean 中,在 @Value 注解中,可以使用 ${} 或 #{}。${} 与 #{} 的区别如下:

(1)@Value("${}"):可以获取对应属性文件中定义的属性值。

(2)@Value("#{}"):表示 SpEl 表达式通常用来获取 bean 的属性,或者调用 bean 的某个方法。

@Value 注解的常用使用方式如下:

注入普通字符串

属性文件内容如下:

1

str1=hello world

Java代码如下:

1

2

3

4

5

6

7

// 直接将字符串赋值给 str 属性

@Value("hello world")

private String str;

// 从属性文件中获取值

@Value("${str1}")

private String str1; // 结果:hello world

如果在属性文件中没有定义 str1 属性名,则 @Value 会抛出如下错误“java.lang.IllegalArgumentException: Could not resolve placeholder 'str1' in value "${str1}"”。我们可以在 str1 属性不存在时,指定默认值,即使没有定义 str2 也不会抛出错误,而是返回默认值。如下:

1

2

@Value("${str2:defaultValue}")

private String str2; // 结果:defaultValue

注入操作系统属性

可以利用 @Value 注入操作系统属性。这里我们不能使用“${}”去获取操作系统属性。如下:

1

2

@Value("${systemProperties['os.name']}")

private String osName;

上面代码将抛出 java.lang.IllegalArgumentException: Could not resolve placeholder 'systemProperties['os.name']' in value "${systemProperties['os.name']}" 错误信息。你需要将 ${} 改为 #{},如下:

1

2

@Value("#{systemProperties['os.name']}")

private String osName; // 结果:Windows 10

注入表达式结果

在 @Value 中,允许我们使用表达式,然后自动计算表达式的结果。将结果复制给指定的变量。如下:

1

2

3

4

5

6

7

// 生成一个随机数

@Value("#{ T(java.lang.Math).random() * 1000.0 }")

private double randomNumber;

// 使用 System 类获取系统环境变量 PATH

@Value("#{ T(java.lang.System).getenv('path') }")

private String path;

注入其他Bean属性

在 @Value 注解中,也可以将其他 bean 的属性值注入到当前 bean。如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

// 其他Bean

@Component

public class OtherBean {

    @Value("OtherBean的NAME属性")

    private String name;

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

}

// 用法

@Component

public class MyBean {

    @Value("#{otherBean.name}")

    private String fromAnotherBean;

    // ...

}

注意,其他 bean 使用 @Component 时,如果没有指定名称,则默认为类名首字母小写,如:otherBean 。当然我们也可以使用 @Component("myName") 形式,指定其他 bean 的名称,此时,访问则需要使用 @Value("#{myName.name}")。如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

// 其他bean,自定义名称为 myBeans

@Component("myBeans")

public class OtherBean2 {

    @Value("OtherBean的NAME属性")

    private String name;

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

}

// 用法

@Component

public class MyBean {

    @Value("#{myBeans.name}")

    private String fromAnotherBean2;

    // ...

}

注入资源

在 @Value 注解中,也可以用来注入资源(Resource),如:文件资源、URL网络资源等。如下:

1

2

3

4

5

6

7

// 注入文件资源

@Value("classpath:props/application.properties")

private Resource fileResource;

// 注入URL资源

@Value("https://www.hxstrive.com")

private Resource urlResource;

完整代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

@Service

public class ValueDemo5 {

    // 注入文件资源

    @Value("classpath:props/application.properties")

    private Resource fileResource;

    // 注入URL资源

    @Value("https://www.hxstrive.com")

    private Resource urlResource;

    public static void main(String[] args) throws Exception {

        ApplicationContext context = new ClassPathXmlApplicationContext(

                "applicationContent-value.xml");

        ValueDemo5 demo = context.getBean(ValueDemo5.class);

        // 输出文件资源内容

        String fileContent = FileUtils.readFileToString(

                demo.getFileResource().getFile(), "UTF-8");

        System.out.println(fileContent);

        // 输出URL资源内容

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        BufferedInputStream inputStream = new BufferedInputStream(

                demo.getUrlResource().getInputStream());

        byte[] buffer = new byte[2048];

        int len;

        while((len = inputStream.read(buffer)) != -1) {

            outputStream.write(buffer, 0, len);

        }

        System.out.println(new String(outputStream.toByteArray(), "UTF-8"));

    }

    public Resource getFileResource() {

        return fileResource;

    }

    public void setFileResource(Resource fileResource) {

        this.fileResource = fileResource;

    }

    public Resource getUrlResource() {

        return urlResource;

    }

    public void setUrlResource(Resource urlResource) {

        this.urlResource = urlResource;

    }

}

@Value注入static属性

一般@Value是使用在非静态方法上的,对于静态方法,以下做法是无效的:

1

2

@Value("${myProp}")

public static String myProp;

如果要向静态属性注入值,可以使用set方法注入,如下:

1

2

3

4

5

6

7

private static String str1;

@Value("${str1}")

public void setStr1(String str1) {

    System.out.println("setStr1 ===> " + str1);

    ValueDemo7.str1 = str1;

}

如果将 setStr1() 方法设置成 static,则 Spring 将抛出如下告警信息:

13:26:34 WARN [org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor] - Autowired annotation is not supported on static methods: public static void com.huangx.spring4.value.ValueDemo7.setStr1(java.lang.String)

引用外部属性文件

通过 @Value 将外部配置文件的值动态注入到Bean中。配置文件主要有两类:

application.properties

在 spring boot 启动时默认加载此文件 application.properties,spring 则需要我们配置:

1

2

3

4

5

6

7

8

9

10

11

12

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

    <!-- 设置如果没有找到匹配的系统属性,是否搜索匹配的系统环境变量 -->

    <property name="searchSystemEnvironment" value="true" />

    <!-- 设置如何检查系统属性(SYSTEM_PROPERTIES_MODE_FALLBACK=1) -->

    <property name="systemPropertiesMode" value="1"/>

    <!-- 加载属性文件,指定属性文件地址,可以指定多个 -->

    <property name="locations">

        <list>

            <value>classpath:props/application.properties</value>

        </list>

    </property>

</bean>

自定义属性文件

自定义属性文件通过 @PropertySource 加载,@PropertySource 可以同时加载多个文件,也可以加载单个文件。如果第一个属性文件和第二属性文件存在相同key,则最后一个属性文件里的key起作用(前面属性文件的key将被覆盖)。加载文件的路径也可以配置变量,如下面实例中 config.properties 属性文件的 ${env.model}(用来指定开发环境模式,如:dev-开发环境、prod-生产环境、test-测试环境)

@ConfigurationProperties#

Spring源码中大量使用了ConfigurationProperties注解,比如server.port就是由该注解获取到的,通过与其他注解配合使用,能够实现Bean的按需配置。

该注解有一个prefix属性,通过指定的前缀,绑定配置文件中的配置,该注解可以放在类上,也可以放在方法上

可以从注解说明中看到,当将该注解作用于方法上时,如果想要有效的绑定配置,那么该方法需要有@Bean注解且所属Class需要有@Configuration注解。

简单一句话概括就是:Sring的有效运行是通过上下文(Bean容器)中Bean的配合完成的,Bean可以简单理解成对象,有些对象需要指定字段内容,那么这些内容我们可以通过配置文件进行绑定,然后将此Bean归还给容器

作用于方法#

比较常见的就是配置读写分离的场景。

配置文件内容#

spring.datasource.druid.write.url=jdbc:mysql://localhost:3306/jpa
spring.datasource.druid.write.username=root
spring.datasource.druid.write.password=1
spring.datasource.druid.write.driver-class-name=com.mysql.jdbc.Driverspring.datasource.druid.read.url=jdbc:mysql://localhost:3306/jpa
spring.datasource.druid.read.username=root
spring.datasource.druid.read.password=1
spring.datasource.druid.read.driver-class-name=com.mysql.jdbc.Driver
@Configuration
public class DruidDataSourceConfig {/*** DataSource 配置* @return*/@ConfigurationProperties(prefix = "spring.datasource.druid.read")@Bean(name = "readDruidDataSource")public DataSource readDruidDataSource() {return new DruidDataSource();}/*** DataSource 配置* @return*/@ConfigurationProperties(prefix = "spring.datasource.druid.write")@Bean(name = "writeDruidDataSource")@Primarypublic DataSource writeDruidDataSource() {return new DruidDataSource();}
}

也许有的人看到这里会比较疑惑,prefix并没有指定配置的全限定名,那它是怎么进行配置绑定的呢?

相信大家肯定了解@Value注解,它可以通过全限定名进行配置的绑定,这里的ConfigurationProperties其实就类似于使用多个@Value同时绑定,绑定的对象就是DataSource类型的对象,而且是 隐式绑定 的,意味着在配置文件编写的时候需要与对应类的字段名称 相同,比如上述spring.datasource.druid.write.url=jdbc:mysql://localhost:3306/jpa ,当然了,你也可以随便写个配置,比如 spring.datasource.druid.write.uuu=www.baidu.com,此时你只需要在注解中加上以下参数即可

以上就完成了多个数据源的配置,为读写分离做了铺垫

作用于Class类及其用法#

配置文件内容#

spring.datasource.url=jdbc:mysql://127.0.0.1:8888/test?useUnicode=false&autoReconnect=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
@ConfigurationProperties(prefix = "spring.datasource")
@Component
public class DatasourcePro {private String url;private String username;private String password;// 配置文件中是driver-class-name, 转驼峰命名便可以绑定成private String driverClassName;private String type;public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getDriverClassName() {return driverClassName;}public void setDriverClassName(String driverClassName) {this.driverClassName = driverClassName;}public String getType() {return type;}public void setType(String type) {this.type = type;}
}
@Controller
@RequestMapping(value = "/config")
public class ConfigurationPropertiesController {@Autowiredprivate DatasourcePro datasourcePro;@RequestMapping("/test")@ResponseBodypublic Map<String, Object> test(){Map<String, Object> map = new HashMap<>();map.put("url", datasourcePro.getUrl());map.put("userName", datasourcePro.getUsername());map.put("password", datasourcePro.getPassword());map.put("className", datasourcePro.getDriverClassName());map.put("type", datasourcePro.getType());return map;}
}
  1. @ConfigurationProperties 和 @value 有着相同的功能,但是 @ConfigurationProperties的写法更为方便
  2. @ConfigurationProperties 的 POJO类的命名比较严格,因为它必须和prefix的后缀名要一致, 不然值会绑定不上, 特殊的后缀名是“driver-class-name”这种带横杠的情况,在POJO里面的命名规则是 下划线转驼峰 就可以绑定成功,所以就是 “driverClassName”

@EnableConfigurationProperties

先说作用:

@EnableConfigurationProperties注解的作用是:使使用 @ConfigurationProperties 注解的类生效。

说明:

如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties 配置文件转化的bean。说白了 @EnableConfigurationProperties 相当于把使用 @ConfigurationProperties 的类进行了一次注入。
测试发现 @ConfigurationProperties 与 @EnableConfigurationProperties 关系特别大。

测试证明:
@ConfigurationProperties@EnableConfigurationProperties 的关系。

@EnableConfigurationProperties 文档中解释:
@EnableConfigurationProperties注解应用到你的@Configuration时, 任何被@ConfigurationProperties注解的beans将自动被Environment属性配置。 这种风格的配置特别适合与SpringApplication的外部YAML配置进行配合使用。

测试发现:
1.使用 @EnableConfigurationProperties 进行注册

@ConfigurationProperties(prefix = "service.properties")
public class HelloServiceProperties {private static final String SERVICE_NAME = "test-service";private String msg = SERVICE_NAME;set/get
}@Configuration
@EnableConfigurationProperties(HelloServiceProperties.class)
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(prefix = "hello", value = "enable", matchIfMissing = true)
public class HelloServiceAutoConfiguration {}@RestController
public class ConfigurationPropertiesController {@Autowiredprivate HelloServiceProperties helloServiceProperties;@RequestMapping("/getObjectProperties")public Object getObjectProperties () {System.out.println(helloServiceProperties.getMsg());return myConfigTest.getProperties();}
}

自动配置设置

service.properties.name=my-test-name
service.properties.ip=192.168.1.1
service.user=kayle
service.port=8080

一切正常,但是 HelloServiceAutoConfiguration 头部不使用 @EnableConfigurationProperties,测访问报错。

2.不使用 @EnableConfigurationProperties 进行注册,使用 @Component 注册

@ConfigurationProperties(prefix = "service.properties")
@Component
public class HelloServiceProperties {private static final String SERVICE_NAME = "test-service";private String msg = SERVICE_NAME;public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}}

Controller 不变,一切正常,如果注释掉 @Component 测启动报错。
由此证明,两种方式都是将被 @ConfigurationProperties 修饰的类,加载到 Spring Env 中。

简单介绍一下@PropertySource和@PropertySources这俩注解
@PropertySource注解概述
@PropertySource注解是Spring 3.1开始引入的配置类注解。通过@PropertySource注解可以将properties配置文件中的key/value存储到Spring的Environment中,Environment接口提供了方法去读取配置文件中的值,参数是properties配置文件中定义的key值。当然了,也可以使用@Value注解用${}占位符为bean的属性注入值。

我们来看一下@PropertySource注解的源代码,如下所示。

从@PropertySource的源码中可以看出,我们可以通过@PropertySource注解指定多个properties文件,使用的形式如下所示。

@PropertySource(value={"classpath:/person.properties", "classpath:/car.properties"})
1
细心的读者可以看到,在@PropertySource注解的上面标注了如下的注解信息。

@Repeatable(PropertySources.class)
1
看到这里,小伙伴们是不是有种恍然大悟的感觉呢?没错,我们也可以使用@PropertySources注解来指定properties配置文件。

@PropertySources注解概述
首先,我们也来看下@PropertySources注解的源码,如下所示。

@PropertySources注解的源码比较简单,只有一个PropertySource[]数组类型的value属性,那我们如何使用@PropertySources注解指定配置文件呢?其实也很简单,使用如下所示的方式就可以了。

@PropertySources(value={
    @PropertySource(value={"classpath:/person.properties"}),
    @PropertySource(value={"classpath:/car.properties"}),
})

是不是很简单呢?接下来,我们就以一个小案例来说明@PropertySource注解的用法。

一个小案例来说明@PropertySource注解的用法
准备工作
首先,我们在工程的src/main/resources目录下创建一个配置文件,例如person.properties,该文件的内容如下所示。

person.nickName=小甜甜
1
然后,我们在Person类中新增一个nickName字段,如下所示。

package com.meimeixia.bean;import org.springframework.beans.factory.annotation.Value;public class Person {@Value("李阿昀")private String name;@Value("#{20-2}")private Integer age;private String nickName; // 昵称public String getNickName() {return nickName;}public void setNickName(String nickName) {this.nickName = nickName;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Person(String name, Integer age) {super();this.name = name;this.age = age;}public Person() {super();// TODO Auto-generated constructor stub}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + ", nickName=" + nickName + "]";}}

目前,我们并没有为Person类的nickName字段赋值,所以,此时Person类的nickName字段的值为空。我们可以运行IOCTest_PropertyValue类中的test01()方法来看下输出结果,如下所示。

可以看到,Person类的nickName字段的值确实输出了null。

使用注解方式获取值

如果我们使用注解的方式,那么该如何做呢?首先,我们需要在MainConfigOfPropertyValues配置类上添加一个@PropertySource注解,如下所示。

package com.meimeixia.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;import com.meimeixia.bean.Person;// 使用@PropertySource读取外部配置文件中的key/value保存到运行的环境变量中,加载完外部的配置文件以后,使用${}取出配置文件中的值
@PropertySource(value={"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {@Beanpublic Person person() {return new Person();}}

这里使用的@PropertySource(value={"classpath:/person.properties"})注解就相当于XML配置文件中使用的<context:property-placeholder location="classpath:person.properties" />。

然后,我们就可以在Person类的nickName字段上使用@Value注解来获取person.properties文件中的值了,如下所示。

@Value("${person.nickName}")
private String nickName; // 昵称

配置完成后,我们再次运行IOCTest_PropertyValue类中的test01()方法来看下输出结果,如下所示。
可以看到,此时Person类的nickName字段已经注入小甜甜这个值了。

使用Environment获取值
上面我已经说过,使用@PropertySource注解读取外部配置文件中的key/value之后,是将其保存到运行的环境变量中了,所以我们也可以通过运行环境来获取外部配置文件中的值。

这里,我们可以稍微修改一下IOCTest_PropertyValue类中的test01()方法,即在其中添加一段使用Environment获取person.properties文件中的值的代码,如下所示。

@Test
public void test01() {printBeans(applicationContext);System.out.println("===================");Person person = (Person) applicationContext.getBean("person");System.out.println(person);ConfigurableEnvironment environment = applicationContext.getEnvironment();String property = environment.getProperty("person.nickName");System.out.println(property);// 关闭容器applicationContext.close();
}

运行以上test01()方法,可以看到输出的结果信息如下所示。

可以看到,使用Environment确实能够获取到person.properties文件中的值。

@Value,@ConfigurationProperties,@EnableConfigurationProperties,@PropertySource,@PropertySources相关推荐

  1. Redis分片主从哨兵集群,原理详解,集群的配置安装,8大数据类型,springboot整合使用

    文章目录 Redis介绍 Redis分片 Redis主从 Redis哨兵 Redis集群 Redis持久化策略 RDB AOF 持久化方案选择 Redis内存策略 LRU算法 LFU算法 Random ...

  2. 都这么卷了,不懂MyBatis插件开发怎么行,教你实现一个MyBatis分页插件

    MyBatis可谓是Java开发工程师必须要掌握的持久层框架,它能够让我们更容易的通过Java代码操作数据库,并且它还有很高的扩展性,我们可以自定义插件,去让MyBatis的功能变的更为强大,本篇文章 ...

  3. 史上最全,Spring Boot入门篇总结,收藏起来慢慢看

    Spring Boot是Spring家族下的一个全新开发框架,其设计目的主要是用来简化Spring应用的创建及开发过程,它提供了自动配置,starter依赖等特性,从而使开发人员从大量的XML配置中解 ...

  4. mysql查询字段大小写结果相同,mysql大小写查询不敏感,mysql5.7查询不区分大小写解决方案。

    下面有两条sql,主键查询,在mysql中查询到的结果相同. SELECT* FROM USER WHEREid = 'EM58hdK4nXC';SELECT* FROM USER WHEREid = ...

  5. kotlin设置CORS跨域资源共享,java设置允许跨域,服务端如何设置 springboot中设置跨域资源共享

    CORS通信过程,都是浏览器或http插件自动完成,不需要 用户/开发人员 参与.对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码是完全一样的.浏览器一旦发现AJAX请求跨源,就会自动添 ...

  6. Postman使用Date数据类型,Postman发送Date类型数据,Postman模拟前端调用

    Postman使用Date数据类型: 在form-data数据类型下,使用 2021/7/7 11:11:00 (yyyy/MM/dd HH:mm:ss) 的数据格式即可 如下: 在json数据格式中 ...

  7. 苹果设备iphone,ipad,macbook网络连接慢,开机开什么卡什么,一步解决

    苹果电脑网络连接慢,开机开什么卡什么??? 网络上的方法一种种,没有一个适用的? 如果你的macbook也是打开就没网,但有一些软件也能用,就是浏览器加载跑条儿,不妨试试! 系统偏好设置-网络-高级- ...

  8. linux/docker个人服务器项目中文变问号??,时间差8小时问题解决方法,最新,最有效

    前段时间在腾讯云上面买了一台个人服务器,在搭建好web项目的时候,在项目中录入中文,全部变成了问号,时间也错了,百思不得其解,后来我尝试着修改docker编码,修改系统语言,都无法改变.后来我把项目从 ...

  9. 学生上课睡觉班主任怎么处理_学生上课睡觉,老师的管与不管,不是你看到的那么简单...

    学生上课睡觉的原因,可以分为正常的和不正常的. 正常的,一种是在天气热的时候,人容易犯困,学生会有上课睡觉的:另一种是因为学习熬夜,导致的白天没精神而上课的时候控制不住自己睡觉. 不正常的,一种是熬夜 ...

最新文章

  1. 如何做一名优秀的博士生--施一公教授
  2. 面试必考-从URL输入到页面展现到底发生了什么
  3. python画散点图程序实例_【112】用python画散点图和直线图的小例子
  4. Linux系统基础.作业
  5. WeihanLi.Npoi 1.20.0 Released
  6. php utf-8编码 正则匹配中文
  7. 被单位开除后,以前的工龄还算吗?
  8. HTM服务器l控件与WEB服务器控件(一)
  9. ADS2015安装包和教程
  10. JavawebJAVAJSP网吧计费管理系统(JSP网吧管理系统)网吧收费管理系统网吧自动计费管理系统
  11. java 使用socks代理
  12. 软件工程实践 Blog17
  13. 简述公网IP、私网IP、动态IP、静态IP
  14. oracle 19c ORA-00942: 表或视图不存在 ORA-02063: 紧接着 line
  15. Revit插件中的“喷淋对齐”“链接CAD”功能操作
  16. 获取Windows操作系统版本和位数
  17. 中国红木家具行业规模及投资风险评估报告2022-2028年
  18. 压缩为王-阿里第五届中间件复赛总结
  19. STM32实战六 PWM加移相正交
  20. AutoSAR系列讲解(实践篇)11.4-NvBlockSwComponents(上)

热门文章

  1. GreenPlum企业应用实践--第五章SQL执行计划
  2. 数据库基础-储存过程和触发器
  3. 开源库OGR的入门知识
  4. 威尔特拉斯定理_数学大师启示录维尔斯特拉斯.pdf
  5. mac键盘图标与对应快捷按键
  6. (列表,字典练习)寻找班级里面名字最长的人
  7. [Jquery]实现三个盒子两两交换的考眼力游戏
  8. 笔记本未启用无线服务器,WiFi无线网络提示未启用DHCP无法上网的解决方法
  9. Win7安装完成后对系统的优化设置
  10. 图像处理计算机考试,计算机图形图像处理试题(A)(含答案).doc