乐趣区

SpringBoot注入数据的方式

关于注入数据说明

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.#{…}和 ${…}混合使用
${…}和 #{…}可以混合使用,如下文代码执行顺序:通过 ${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 表达式,并将内容赋值给属性
${…} 主要用于加载外部属性文件中的值
#{…} 和 &dollar;{…} 可以混合使用,但是必须 #{}外面,&dollar;{}在里面

4.@Value 获取值和 @ConfigurationProperties 获取值比较

@ConfigurationProperties
@Value

功能
批量注入配置文件中的属性
一个个指定

松散绑定(松散语法)
支持
不支持

SpEL
不支持
支持

JSR303 数据校验
支持
不支持

复杂类型封装
支持
不支持

配置文件 yml 还是 properties 他们都能获取到值;

如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用 @Value;
如果说,我们专门编写了一个 javaBean 来和配置文件进行映射,我们就直接使用 @ConfigurationProperties;

关于数据校验的部分代码
@Component
@ConfigurationProperties(prefix = “person”)
@Validated
public class Person {
//lastName 必须是邮箱格式
@Email
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]}

退出移动版