写在前面

在之前的文章中,我们探讨了如何向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. StringBuilder的线程为什么不安全

    StringBuffer和StringBuilder的区别在哪里? StringBuffer是线程安全的,StringBuilder是线程不安全的. 那么StringBuilder不安全在哪里?在想这 ...

  2. SQL server 导入数据 (excel导入到SQL server数据库)

    打开数据库SQL server ,右键数据库,任务,导入数据 点击下一步 选择数据源类型 选择路径,点击下一步 选择将要生成的类型 选择登陆方式 选中,点击下一步 点击编辑映射可以修改将要生成的表,点 ...

  3. Java实现LeetCode_0013_RomanToInteger

    package javaLeetCode.primary; import java.util.HashMap; import java.util.Map; import java.util.Scann ...

  4. java实现第七届蓝桥杯平方圈怪

    平方圈怪 题目描述 如果把一个正整数的每一位都平方后再求和,得到一个新的正整数. 对新产生的正整数再做同样的处理. 如此一来,你会发现,不管开始取的是什么数字, 最终如果不是落入1,就是落入同一个循环 ...

  5. C#基础篇——泛型

    前言 在开发编程中,我们经常会遇到功能非常相似的功能模块,只是他们的处理的数据不一样,所以我们会分别采用多个方法来处理不同的数据类型.但是这个时候,我们就会想一个问题,有没有办法实现利用同一个方法来传 ...

  6. EasyARM-iMX257 linux两年前的笔记

    我依然清晰的记得刚拿到Imx283 257的情景,兴奋中充满忧虑,对操作系统的概念只知一二,不知三四!!周立功出品的资料我一直觉得是比较精品的,同样这款iMX283配套的文档资料(v1.04)也是比较 ...

  7. 对SSH框架的理解

    首先是对struts的理解.struts是把servlet.jsp以及众多标签库整合在一起的开源web框架,他实现了mvc设计模式.Struts实际上就是对MVC的各部件提供了现成的实现组件.Stru ...

  8. KVM在线扩展虚拟机内存

    环境介绍 在KVM下有一台虚拟机内存不够需要扩展内存.宿主机地址是192.168.1.28.我需要扩展的虚拟机是centos1708vm03. 1.登陆上宿主机查看虚拟机配置 virsh dumpxm ...

  9. 将XML转换为JSON并强制数组

    string xml = @"<person id='1'> <name>Alan</name> <url>http://www.google ...

  10. win10系统下office 2019激活

    1.新建一个文本文件,创建批处理文件office.bat @echo off (cd /d "%~dp0")&&(NET FILE||(powershell sta ...