关于注入数据说明
1. 不通过配置文件注入数据
通过 @Value 将外部的值动态注入到 Bean 中, 使用的情况有:
注入普通字符串
注入操作系统属性
注入表达式结果
注入其他 Bean 属性: 注入 Student 对象的属性 name
注入文件资源
注入 URL 资源
辅助代码
- package com.hannpang.model;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
- @Component(value = "st")// 对 student 进行实例化操作
- public class Student {
- @Value("悟空")
- private String name;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
测试 @Value 的代码
- package com.hannpang.model;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.core.io.Resource;
- import org.springframework.stereotype.Component;
- @Component
- public class SimpleObject {
- @Value("注入普通字符串")
- private String normal;
- // 关于属性的 KEY 可以查看 System 类说明
- @Value("#{systemProperties['java.version']}")//-->使用了 SpEL 表达式
- private String systemPropertiesName; // 注入操作系统属性
- @Value("#{T(java.lang.Math).random()*80}")// 获取随机数
- private double randomNumber; // 注入表达式结果
- @Value("#{1+2}")
- private double sum; // 注入表达式结果 1+2 的求和
- @Value("classpath:os.yaml")
- private Resource resourceFile; // 注入文件资源
- @Value("http://www.baidu.com")
- private Resource testUrl; // 注入 URL 资源
- @Value("#{st.name}")
- private String studentName;
- // 省略 getter 和 setter 方法
- @Override
- public String toString() {
- return "SimpleObject{" +
- "normal='" + normal + '\'' +
- ", systemPropertiesName='" + systemPropertiesName + '\'' +
- ", randomNumber=" + randomNumber +
- ", sum=" + sum +
- ", resourceFile=" + resourceFile +
- ", testUrl=" + testUrl +
- ", studentName='" + studentName + '\'' +
- '}';
- }
- }
Spring 的测试代码
- package com.hannpang;
- import com.hannpang.model.SimpleObject;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.test.context.junit4.SpringRunner;
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class Demo04BootApplicationTests {
- @Autowired
- private SimpleObject so;
- @Test
- public void contextLoads() {
- System.out.println(so);
- }
- }
运行结果为: SimpleObject{normal='注入普通字符串', systemPropertiesName='1.8.0_172', randomNumber=56.631954541947266, sum=3.0, resourceFile=class path resource [os.YAML], testUrl=URL [http://www.baidu.com], studentName='悟空'}
2. 通过配置文件注入数据
通过 @Value 将外部配置文件的值动态注入到 Bean 中. 配置文件主要有两类:
application.properties,application.YAML application.properties 在 spring boot 启动时默认加载此文件
自定义属性文件. 自定义属性文件通过 @PropertySource 加载.@PropertySource 可以同时加载多个文件, 也可以加载单个文件. 如果相同第一个属性文件和第二属性文件存在相同 key, 则最后一个属性文件里的 key 启作用. 加载文件的路径也可以配置变量, 如下文的 ${anotherfile.configinject}, 此值定义在第一个属性文件 config.properties
在 application.properties 中加入如下测试代码
App.name = 一步教育
在 resources 下面新建第一个属性文件 config.properties 内容如下
book.name = 西游记
anotherfile.configinject=system
在 resources 下面新建第二个属性文件 config_system.properties 内容如下
我的目的是想 system 的值使用第一个属性文件中定义的值
book.name.author = 吴承恩
下面通过 @Value("${app.name}")语法将属性文件的值注入 bean 属性值, 详细代码见:
- package com.hannpang.test;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.context.annotation.PropertySource;
- import org.springframework.core.env.Environment;
- import org.springframework.stereotype.Component;
- @Component
- @PropertySource(value = {"classpath:config.properties","classpath:config_${anotherfile.configinject}.properties"})
- public class LoadPropsTest {
- @Value("${app.name}")
- private String appName; // 这里的值来自 application.properties,spring boot 启动时默认加载此文件
- @Value("${book.name}")
- private String bookName; // 注入第一个配置外部文件属性
- @Value("${book.name.author}")
- private String author; // 注入第二个配置外部文件属性
- @Autowired
- private Environment env; // 注入环境变量对象, 存储注入的属性值
- // 省略 getter 和 setter 方法
- public void setAuthor(String author) {
- this.author = author;
- }
- @Override
- public String toString(){
- StringBuilder sb = new StringBuilder();
- sb.append("bookName=").append(bookName).append("\r\n")
- .append("author=").append(author).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.author")).append("\r\n");
- return sb.toString();
- }
- }
测试代码
- package com.hannpang;
- import com.hannpang.model.SimpleObject;
- import com.hannpang.test.LoadPropsTest;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.test.context.junit4.SpringRunner;
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class Demo04BootApplicationTests {
- @Autowired
- private LoadPropsTest lpt;
- @Test
- public void loadPropertiesTest() {
- System.out.println(lpt);
- }
- }
运行结果为:
bookName = 西游记
author = 吴承恩
appName = 一步教育
env=StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, MapPropertySource {name='Inlined Test Properties'}, MapPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.properties]'}, ResourcePropertySource {name='class path resource [config_system.properties]'}, ResourcePropertySource {name='class path resource [config.properties]'}]}
env = 吴承恩
3. #{...}和 ${...}的区别演示
A.${...}的用法
{}里面的内容必须符合 SpEL 表达式, 通过 @Value("${app.name}")可以获取属性文件中对应的值, 但是如果属性文件中没有这个属性, 则会报错. 可以通过赋予默认值解决这个问题, 如 @Value("${app.name: 胖先森}")
部分代码
- // 如果属性文件没有 App.name, 则会报错
- // @Value("${app.name}")
- // private String name;
- // 使用 App.name 设置值, 如果不存在则使用默认值
- @Value("${app.name: 胖先森}")
- private String name;
B.#{...}的用法
部分代码直接演示
- // SpEL: 调用字符串 Hello World 的 concat 方法
- @Value("#{'Hello World'.concat('!')}")
- private String helloWorld;
- // SpEL: 调用字符串的 getBytes 方法, 然后调用 length 属性
- @Value("#{'Hello World'.bytes.length}")
- private String helloWorldbytes;
C.#{...}和 ${...}混合使用
${...}和 #{...}可以混合使用, 如下文代码执行顺序: 通过 ${ http://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 会尝试从属性中查找 #{'HelloWorld'.concat('_')}, 那么肯定找到, 由上文已知如果找不到, 然后报错. 所以 ${}在外面,#{}在里面是非法操作
D. 用法总结
#{...} 用于执行 SpEl 表达式, 并将内容赋值给属性
${...} 主要用于加载外部属性文件中的值
#{...} 和 ${...} 可以混合使用, 但是必须 #{}外面,${}在里面
4.@Value 获取值和 @ConfigurationProperties 获取值比较
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303 数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
配置文件 YAML 还是 properties 他们都能获取到值;
如果说, 我们只是在某个业务逻辑中需要获取一下配置文件中的某项值, 使用 @Value;
如果说, 我们专门编写了一个 javaBean 来和配置文件进行映射, 我们就直接使用 @ConfigurationProperties;
关于数据校验的部分代码
- @Component
- @ConfigurationProperties(prefix = "person")
- @Validated
- public class Person {
- //lastName 必须是邮箱格式
- private String lastName;
5. @ImportResource 引入配置文件
不推荐的使用方式
Spring Boot 里面没有 Spring 的配置文件, 我们自己编写的配置文件, 也不能自动识别;
想让 Spring 的配置文件生效, 加载进来;@ImportResource 标注在一个配置类上
@ImportResource(locations = {"classpath:beans.xml"})
导入 Spring 的配置文件让其生效
编写配置文件信息
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
- <bean id="helloService" class="com.hanpang.springboot.service.HelloService"></bean>
- </beans>
大概了解就好, 我们基本上不使用这种方式
6.@Configuration 注解
SpringBoot 推荐给容器中添加组件的方式; 推荐使用全注解的方式
1, 配置类 @Configuration 作用于类上, 相当于一个 xml 配置文件
2, 使用 @Bean 给容器中添加组件, 作用于方法上
- /**
- * @Configuration: 指明当前类是一个配置类; 就是来替代之前的 Spring 配置文件
- *
- * 在配置文件中用 < bean><bean/>标签添加组件
- * <bean id="helloService" class="com.hanpang.springboot.service.HelloService"></bean>
- */
- @Configuration
- public class MyAppConfig {
- // 将方法的返回值添加到容器中; 容器中这个组件默认的 id 就是方法名
- @Bean
- public HelloService helloService02(){
- System.out.println("配置类 @Bean 给容器中添加组件了...");
- return new HelloService();
- }
- }
使用 Bean 注入太麻烦, 我们更加喜欢使用扫描的方式
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.Configuration;
- import com.wx.dao.IUserDao;
- import com.wx.dao.UserDaoImpl;
- // 通过该注解来表明该类是一个 Spring 的配置, 相当于一个传统的 ApplicationContext.xml
- @Configuration
- // 相当于配置文件里面的 < context:component-scan/>标签, 扫描这些包下面的类的注解
- @ComponentScan(basePackages="com.hanpang.dao,com.hanpang.service")
- public class SpringConfig {
- // 通过该注解来表明是一个 Bean 对象, 相当于 xml 中的 < bean>
- //bean 的 id 值默认是方法名 userDao
- /*
- @Bean
- public HelloService helloService02(){
- System.out.println("配置类 @Bean 给容器中添加组件了...");
- return new HelloService();
- }
- */
- }
附录
随机数
- ${
- random.value
- },${
- random.int
- },${
- random.long
- }
- ${
- random.int(10)
- },${
- random.int[1024,65536]
- }
来源: https://juejin.im/post/5c1f119ee51d4568f03b8857