写在前面

在之前的文章中,我们探讨了如何向Spring的IOC容器中注册bean组件,讲解了有关bean组件的生命周期的知识。今天,我们就来一起聊聊@Value注解的用法。

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

@Value注解

Spring中的@Value注解可以为bean中的属性赋值。我们先来看看@Value注解的源码,如下所示。

package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
String value();
}

从@Value注解的源码,我们可以看出:@Value注解可以标注在字段、方法、参数、注解上,在程序运行期间生效。

@Value注解用法

1.不通过配置文件注入属性的情况

通过@Value将外部的值动态注入到Bean中,使用的情况有:

  • 注入普通字符串
@Value("normal")
private String normal; // 注入普通字符串
  • 注入操作系统属性
@Value("#{systemProperties['os.name']}")
private String systemPropertiesName; // 注入操作系统属性
  • 注入表达式结果
@Value("#{ T(java.lang.Math).random() * 100.0 }")
private double randomNumber; //注入表达式结果
  • 注入其他Bean属性
@Value("#{person.name}")
private String name; // 注入其他Bean属性:注入person对象的属性name
  • 注入文件资源
@Value("classpath:io/mykit/spring/config/config.properties")
private Resource resourceFile; // 注入文件资源
  • 注入URL资源
@Value("http://www.baidu.com")
private Resource url; // 注入URL资源

2.通过配置文件注入属性的情况

通过@Value(“${app.name}”)语法将属性文件的值注入到bean的属性中,如下所示。

@Component
// 引入外部配置文件组:${app.configinject}的值来自config.properties。
// 如果相同
@PropertySource({"classpath:io/mykit/spring/config/config.properties",
"classpath:io/mykit/spring/config/config_${anotherfile.configinject}.properties"})
public class ConfigurationFileInject{
// 这里的值来自application.properties,spring boot启动时默认加载此文件
@Value("${app.name}")
private String appName; // 注入第一个配置外部文件属性
@Value("${book.name}")
private String bookName; // 注入第二个配置外部文件属性
@Value("${book.name.placeholder}")
private String bookNamePlaceholder; // 注入环境变量对象,存储注入的属性值
@Autowired
private Environment env; public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("bookName=").append(bookName).append("\r\n")
.append("bookNamePlaceholder=").append(bookNamePlaceholder).append("\r\n")
.append("appName=").append(appName).append("\r\n")
.append("env=").append(env).append("\r\n")
// 从eniroment中获取属性值
.append("env=").append(env.getProperty("book.name.placeholder")).append("\r\n");
return sb.toString();
}
}

3.@Value中#{..}和${...}的区别

我们这里提供一个测试属性文件:advance_value_inject.properties,大致的内容如下所示。

server.name=server1,server2,server3
author.name=binghe

测试类AdvanceValueInject:引入advance_value_inject.properties文件,作为属性的注入

@Component
@PropertySource({"classpath:io/mykit/spring/config/advance_value_inject.properties"})
public class AdvanceValueInject {
...
}

${...}的用法

{}里面的内容必须符合SpEL表达式, 通过@Value(“${spelDefault.value}”)可以获取属性文件中对应的值,但是如果属性文件中没有这个属性,则会报错。可以通过赋予默认值解决这个问题,如下所示。

@Value("${author.name:binghe}")

上述代码的含义表示向bean的属性中注入配置文件中的author.name属性的值,如果配置文件中没有author.name属性,则向bean的属性中注入默认值binghe。例如下面的代码片段。

@Value("${author.name:binghe}")
private String name;

#{…}的用法

// SpEL:调用字符串Hello World的concat方法
@Value("#{'Hello World'.concat('!')}")
private String helloWorld; // SpEL: 调用字符串的getBytes方法,然后调用length属性
@Value("#{'Hello World'.bytes.length}")
private String helloWorldbytes;

${…}和#{…}混合使用

${...}和#{...}可以混合使用,如下文代码执行顺序:通过${server.name}从属性文件中获取值并进行替换,然后就变成了 执行SpEL表达式{‘server1,server2,server3’.split(‘,’)}。

// SpEL: 传入一个字符串,根据","切分后插入列表中, #{}和${}配置使用(注意单引号,注意不能反过来${}在外面,#{}在里面)
@Value("#{'${server.name}'.split(',')}")
private List<String> servers;

在上文中#{}在外面,${}在里面可以执行成功,那么反过来是否可以呢?也就是说能否让${}在外面,#{}在里面,如下代码所示。

// SpEL: 注意不能反过来${}在外面,#{}在里面,这个会执行失败
@Value("${#{'HelloWorld'.concat('_')}}")
private List<String> servers2;

答案是不能。因为Spring执行${}时机要早于#{},当Spring执行外层的${}时,内部的#{}为空,所以会执行失败!

@Value注解用法小结:

  • #{…} 用于执行SpEl表达式,并将内容赋值给属性。
  • ${…} 主要用于加载外部属性文件中的值。
  • #{…} 和${…} 可以混合使用,但是必须#{}外面,${}在里面。

@Value注解案例

这里,我们还是以一个小案例的形式来说明。

首先,我们来创建一个Person类作为测试的bean组件,如下所示。

package io.mykit.spring.plugins.register.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
/**
* @author binghe
* @version 1.0.0
* @description 测试实体类
*/
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {
private static final long serialVersionUID = 7387479910468805194L;
private String name;
private Integer age;
}

接下来,创建一个新的配置类PropertyValueConfig,用来配置Spring的bean组件,我们在PropertyValueConfig类中将Person类的对象注册到IOC容器中,如下所示。

package io.mykit.spring.plugins.register.config;
import io.mykit.spring.plugins.register.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author binghe
* @version 1.0.0
* @description 测试属性赋值
*/
@Configuration
public class PropertyValueConfig {
@Bean
public Person person(){
return new Person();
}
}

我们再来创建一个测试类PropertyValueTest,在PropertyValueTest类中创建测试方法testPropertyValue01(),并在testPropertyValue01()方法中通过PropertyValueConfig类创建AnnotationConfigApplicationContext对象,打印出目前IOC容器中存在的bean名称,如下所示。

package io.mykit.spring.test;
import io.mykit.spring.plugins.register.config.PropertyValueConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Arrays;
/**
* @author binghe
* @version 1.0.0
* @description 测试bean的生命周期
*/
public class PropertyValueTest {
@Test
public void testPropertyValue01(){
//创建IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PropertyValueConfig.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
}
}

此时,我们运行PropertyValueTest类的testPropertyValue01()方法,输出的结果信息如下所示。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
propertyValueConfig
person

从输出的结果信息中,可以看出,IOC容器中除了Spring框架注册的bean之外,还包含我们自己向IOC容器中注册的bean组件:propertyValueConfig和person。

接下来,我们改造下PropertyValueTest类的testPropertyValue01()方法,输出Person对象的信息,如下所示。

package io.mykit.spring.test;
import io.mykit.spring.plugins.register.bean.Person;
import io.mykit.spring.plugins.register.config.PropertyValueConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Arrays;
/**
* @author binghe
* @version 1.0.0
* @description 测试bean的生命周期
*/
public class PropertyValueTest {
@Test
public void testPropertyValue01(){
//创建IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PropertyValueConfig.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println); System.out.println("================================");
Person person = (Person) context.getBean("person");
System.out.println(person);
}
}

接下来,再次运行PropertyValueTest类的testPropertyValue01()方法,输出的结果信息如下所示。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
propertyValueConfig
person
================================
Person(name=null, age=null)

可以看到,向IOC容器中注册的Person对象的name属性为null,age属性为null。那如何向Person对象的name属性和age属性赋值呢?此时,Spring中的@Value注解就派上了用场。

如果我们通过XML文件为bean的属性赋值,则可以通过如下配置的方式实现。

<bean id = "person" class="io.mykit.spring.plugins.register.bean.Person">
<property name="name" value="binghe"></property>
<property name="age" value="18"></property>
</bean>

如果使用注解该如何实现呢?别急,往下看!

我们可以在Person类的属性上使用@Value注解为属性赋值,如下所示。

package io.mykit.spring.plugins.register.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import java.io.Serializable;
/**
* @author binghe
* @version 1.0.0
* @description 测试实体类
*/
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {
private static final long serialVersionUID = 7387479910468805194L;
@Value("binghe")
private String name;
@Value("#{20-2}")
private Integer age;
}

此时,我们再次运行PropertyValueTest类的testPropertyValue01()方法,输出的结果信息如下所示。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
propertyValueConfig
person
================================
Person(name=binghe, age=18)

可以看到,使用@Value注解已经向Person对象的name属性中注入了binghe,向age属性中注入了18。

好了,咱们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一起学习一起进步!!

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

写在最后

如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发不再迷茫。

部分参考:blog.csdn.net/hry2015/article/details/72453920

【Spring注解驱动开发】如何使用@Value注解为bean的属性赋值,我们一起吊打面试官!的更多相关文章

  1. 【Spring注解驱动开发】在@Import注解中使用ImportSelector接口导入bean

    写在前面 在上一篇关于Spring的@Import注解的文章<[Spring注解驱动开发]使用@Import注解给容器中快速导入一个组件>中,我们简单介绍了如何使用@Import注解给容器 ...

  2. 【Spring注解驱动开发】在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean

    写在前面 在前面的文章中,我们学习了如何使用@Import注解向Spring容器中导入bean,可以使用@Import注解快速向容器中导入bean,小伙伴们可以参见<[Spring注解驱动开发] ...

  3. 【Spring注解驱动开发】使用@Scope注解设置组件的作用域

    写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...

  4. 【Spring注解驱动开发】使用@Lazy注解实现懒加载

    写在前面 Spring在启动时,默认会将单实例bean进行实例化,并加载到Spring容器中.也就是说,单实例bean默认在Spring容器启动的时候创建对象,并将对象加载到Spring容器中.如果我 ...

  5. 【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件

    写在前面 我们可以将一些bean组件交由Spring管理,并且Spring支持单实例bean和多实例bean.我们自己写的类,可以通过包扫描+标注注解(@Controller.@Servcie.@Re ...

  6. [原创]java WEB学习笔记98:Spring学习---Spring Bean配置及相关细节:如何在配置bean,Spring容器(BeanFactory,ApplicationContext),如何获取bean,属性赋值(属性注入,构造器注入),配置bean细节(字面值,包含特殊字符,引用bean,null值,集合属性list map propert),util 和p 命名空间

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  7. 1、课程简介-Spring 注解驱动开发

    1.课程简介-Spring 注解驱动开发

  8. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

  9. 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!

    写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...

  10. 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则

    写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...

随机推荐

  1. Java实现 蓝桥杯VIP 算法提高 排队打水问题

    算法提高 排队打水问题 时间限制:1.0s 内存限制:256.0MB 问题描述 有n个人排队到r个水龙头去打水,他们装满水桶的时间t1.t2----tn为整数且各不相等,应如何安排他们的打水顺序才能使 ...

  2. Java实现计数排序

    1 问题描述 给定一组数据,请使用计数排序,得到这组数据从小到大的排序序列. 2 解决方案 2.1比较计数排序 下面算法的时间复杂度为O(n^2),空间复杂度为O(n).此方法对于任意一组数据均可排序 ...

  3. surface go重新做系统

    此教程适用于使用U盘恢复介质来恢复Surface Go二合一设备系统SurfaceGo_BMR_45_64_1.011.2.zip 大致两个步骤 一.制作U盘恢复介质 下载适用于自己平板的恢复镜像文件 ...

  4. [leetcode] 股票问题

    参考文章: [1] 团灭 LeetCode 股票买卖问题 [2] Most consistent ways of dealing with the series of stock problems 其 ...

  5. 手把手教你用JS/Vue/React实现幸运水果机(80后情怀之作)

    项目体验地址 免费视频教程 分别使用原生JS,Vue和React,手把手教你开发一个H5小游戏,快速上手Vue和React框架的使用. 项目截图 在线体验 在线体验 游戏介绍 幸运水果机是一款街机游戏 ...

  6. 使用CURL和火车头软件采集搜狐文章

    直接上代码: //参数1:访问的URL,参数2:post数据(不填则为GET),参数3:提交的$cookies,参数4:是否返回$cookies function curl_request($url, ...

  7. [转] Linux下用文件IO的方式操作GPIO(/sys/class/gpio)

    点击阅读原文 一.概述 通过 sysfs 方式控制 GPIO,先访问 /sys/class/gpio 目录,向 export 文件写入 GPIO 编号,使得该 GPIO 的操作接口从内核空间暴露到用户 ...

  8. python常用模块-os

    得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd() 返回指定目录下的所有文件和目录名:os.listdir() 函数用来删除一个文件:os.remove() 删除多个目录 ...

  9. (一)HttpClient Get请求

    原文链接:https://blog.csdn.net/justry_deng/article/details/81042379 HttpClient的主要功能: 实现了所有 HTTP 的方法(GET. ...

  10. c常用函数-strlen

    strlen 返回字符串长度 Action() { char test[] = "yyyHHHJJJoo"; unsigned int a; a = strlen(test); l ...