spring注解开发3——属性赋值与自动装配

本文最后更新于:2022年5月12日 下午

摘要:介绍了Spring容器给属性赋值和自动装配

属性赋值

使用@Value注解

通过在需要赋值的属性上加@Value注解,然后设置注解的参数value完成对属性的赋值。参数value的值是一个字符串,字符串的引号内可以写:

  • 基本数值:如基本的数据类型
  • SpEL:#{}
  • ${}:取出配置文件【properties】中的值(运行环境变量里面的值)

开发步骤

  1. 创建配置文件

在resources文件夹下创建一个properties文件,在文件夹中写要给属性赋的值

1
person.sex=
  1. 创建一个Person类

该类有三个属性,分别是name: String、a ge: int、sex: String。分别采用上述三种方式进行赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.shg.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;

/**
* @author: shg
* @create: 2022-05-12 12:46 上午
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
@Value(value = "property")
private String name;
@Value(value = "#{20-2}")
private int age;
@Value(value = "${person.sex}")
private String sex;
}

  1. 新建一个配置类
  • 配置类中首先使用@Configuration注解将该类声明为配置类

  • 使用@PropertySource注解导入配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.shg.config;

import com.shg.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
* @author: shg
* @create: 2022-05-12 12:47 上午
*/
@Configuration
@PropertySource(value = {"classpath:/application.properties"})
public class MyConfig {

@Bean
public Person person() {
return new Person();
}
}

  1. 查看输出结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.shg.test;

import com.shg.bean.Person;
import com.shg.config.MyConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
* @author: shg
* @create: 2022-05-12 12:49 上午
*/

public class PropertyTest {

@Test
public void testPropertySet() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Person person = context.getBean(Person.class);
System.out.println("person = " + person); // person = Person(name=property, age=18, sex=男)
}
}

自动装配

Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值,主要有@Autowired、@Resource、@Inject三种方式。

标记属性完成自动装配

@Autowired自动注入原理
  1. 默认优先 按照类型去容器中找对应的组件:通过调用getBean(Class var1)方法

  2. 如果容器中有多个相同类型的组件,再将属性的名称作为组件的id去容器中查找:通过调用getBean(String var1)方法

  3. 还可以通过@Qualifier注解指定需要自动装配的组件id

  4. 自动装配一定要将属性赋值好,没有则会报错。可以通过@Autowired(required=false)设置容器中有依赖的bean则自动装配,没有则不装配。

  5. 还可以使用@Primary注解,来指定进行装配的首选bean。

开发步骤
  1. 分别创建三个类如下:

UserDao类,依赖Person类,但是Person类并没有注册到容器中,因此@Autowired注解的required设置为 false防止报错,不需要重写toString方法,便于查看实例的地址判断是同一个实例,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.shg.dao;

import com.shg.bean.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

/**
* @author: shg
* @create: 2022-05-12 1:33 上午
*/
@Repository
public class UserDao {
@Autowired(required = false)
private Person person;
}

UserService类,依赖UserDao,且属性名称设置为userDao0,可以排除是根据属性名进行注入的,重写toString(只需要加上@Data注解即可)方法,可以查看到里面的UserDao实例,并且再在UserService中声明一个属性label以区分不同的UserService实例。

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
package com.shg.service;

import com.shg.dao.UserDao;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* @author: shg
* @create: 2022-05-12 1:33 上午
*/
@Service
@Data
@NoArgsConstructor
public class UserService {

private int label;

public UserService(int label) {
this.label = label;
}

@Autowired
private UserDao userDao0;
}

Usercontroller类,依赖UserService,为了演示根据属性名称和根据@Qualifier设置进行注入,此类中声明了两个UserService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.shg.controller;

import com.shg.service.UserService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

/**
* @author: shg
* @create: 2022-05-12 1:37 上午
*/
@Controller
@Data
public class UserController {
@Autowired
private UserService userService1;

@Autowired
@Qualifier(value = "userService")
private UserService userService2;

}

  1. 新建一个配置类

给类中,也创建了一个UserService实例,该实例的id为userService1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.shg.config;

import com.shg.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
* @author: shg
* @create: 2022-05-12 1:37 上午
*/
@Configuration
@ComponentScan(value = {"com.shg.dao", "com.shg.service", "com.shg.controller"})
public class MyConfig1 {

@Bean
public UserService userService1() {
return new UserService(1);
}
}

  1. 查看输出结果
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
package com.shg.test;

import com.shg.config.MyConfig1;
import com.shg.dao.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Arrays;

/**
* @author: shg
* @create: 2022-05-12 1:39 上午
*/
public class AutowiredTest {

@Test
public void testAutowired() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig1.class);
Arrays.stream(context.getBeanDefinitionNames())
.filter(name -> name.contains("user"))
.map(name -> context.getBean(name))
.forEach(System.out::println);
}
}

结果如下:

userDao的地址为@51bd8b5c,所有的UserService实例中注入的userDao均为这一个

UserController有2个UserService实例:

  • userService1对应的是根据属性名注入的label为1的UserService实例
  • userService2对应的是根据@Qualifier(value = “userService”)注入label为0的UserService实例
1
2
3
4
com.shg.dao.UserDao@51bd8b5c
UserService(label=0, userDao0=com.shg.dao.UserDao@51bd8b5c)
UserController(userService1=UserService(label=1, userDao0=com.shg.dao.UserDao@51bd8b5c), userService2=UserService(label=0, userDao0=com.shg.dao.UserDao@51bd8b5c))
UserService(label=1, userDao0=com.shg.dao.UserDao@51bd8b5c)
@Resource与@Inject

@Resource是JSR250规范的注解,@Inject是JSR330规范的注解,与@Autowired(Spring定义的)的区别如下:

彩蛋:意大利杯决赛 尤文图斯 vs 国际米兰 半场1:0

@Resource

  • 可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配
  • 没有支持@Primary功能,也没有支持@Autowired(required=false)

@Inject

  • 首先需要导入java x.inject包
  • 支持@Primary功能,没有支持@Autowired(required=false)

彩蛋:意大利杯决赛 尤文图斯 vs 国际米兰 全场2:2进入加时赛

彩蛋:意大利杯决赛 尤文图斯 vs 国际米兰 加时2:4,国际米兰夺冠

FORZA INTER

标记其他位置完成自动装配

@Autowired可以标记在构造器、参数、方法、属性位置,都是从容器中获取参数组件的值

  1. 标注在方法位置:@Bean+方法参数,参数从容器中获取
  2. 标注在都早起上
  • 如果再见只有一个又惨构造器,这个有参构造器的@Autowired可以省略
  1. 放在参数位置

使用Spring容器底层的组件

自定义组件如果想用Spring容器底层的一些组件如:ApplicationContext、BeanFactory等,可以通过自定义组件实现xxxAware接口,在创建对象的时候,会调用接口规定的方法注入相关组件。

@Profile环境标识

Profile:Spring提供的可以根据当前环境,动态激活和切换一系列组件的功能

@Profile注解:指定组件在哪个环境的情况下才能被注册到容器中。

  • 没有@Profile标识的bean,任何环境下都能注册这个bean

  • 加了@Profile的bean,只有在标识的环境下才能注册到容器中,默认注册标识为“default”

  • 如果@Profile写在类上,则只有指定环境的时候,整个配置类里面的所有配置才能生效

激活环境

  1. 使用命令行动态参数:在虚拟机参数位置设置-Dspring.profile.active=test/prod/dev
  2. 使用代码的方式激活某种环境
开发步骤——以配置数据库连接池为例
  1. 导入依赖
1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
  1. 创建配置文件
1
2
3
4
5
6
db.user=root
db.password=root
db.driverClass=com.mysql.cj.jdbc.Driver
db.url.test=jdbc:mysql://localhost:3306/library
db.url.dev=jdbc:mysql://localhost:3306/cloud_user
db.url.prod=jdbc:mysql://localhost:3306/community
  1. 创建配置类
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package com.shg.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

/**
* @author: shg
* @create: 2022-05-12 2:23 下午
*/
@Configuration
@PropertySource(value = {"classpath:/db.properties"})
public class DataSourceConfig implements EmbeddedValueResolverAware {

@Value(value = "${db.user}")
private String user;
@Value(value = "${db.password}")
private String password;
@Value(value = "${db.driverClass}")
private String driverClass;

private StringValueResolver resolver;

@Profile(value = "default")
@Bean(value = "defaultDataSource")
public DataSource dataSourceDefault(@Value(value = "${db.url.test}") String url) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl(url);
dataSource.setDriverClass(driverClass);
return dataSource;
}

@Profile(value = "test")
@Bean(value = "testDataSource")
public DataSource dataSourceTest(@Value(value = "${db.url.test}") String url) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl(url);
dataSource.setDriverClass(driverClass);
return dataSource;
}

@Profile(value = "dev")
@Bean(value = "devDataSource")
public DataSource dataSourceDev(@Value(value = "${db.password}") String password) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
String url = resolver.resolveStringValue("${db.url.dev}");
dataSource.setJdbcUrl(url);
dataSource.setDriverClass(driverClass);
return dataSource;
}

@Profile(value = "prod")
@Bean(value = "prodDataSource")
public DataSource dataSourceProd(@Value(value = "${db.password}") String password) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
String url = resolver.resolveStringValue("${db.url.prod}");
dataSource.setJdbcUrl(url);
dataSource.setDriverClass(driverClass);
return dataSource;
}

@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
}

  1. 查看运行结果
  • 不设置任何虚拟机参数
1
2
3
4
5
6
7
8
9
10
@Test
public void testProfile() {
ApplicationContext context = new AnnotationConfigApplicationContext(DataSourceConfig.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names)
.filter(name -> name.contains("Source"))
.forEach(System.out::println);
// dataSourceConfig
// defaultDataSource
}
  • 通过代码的方式设置为test环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testTestProfile() {
// 创建一个AnnotationConfigApplicationContext容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 设置需要激活的环境
context.getEnvironment().setActiveProfiles("test");
// 注册配置类
context.register(DataSourceConfig.class);
// 刷新容器
context.refresh();
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names)
.filter(name -> name.contains("Source"))
.forEach(System.out::println);
// dataSourceConfig
// testDataSource
}

总结

  1. 属性赋值

通过@Value完成属性赋值,注解中参数value可以是一个字符串、也可以是SpEL表达式、还可以是${}。

  1. 自动装配

自动装配的注解有@Autowired、@Resource、@Inject,如果需要注入Spring底层的组件,如xxxAware,可以通过实现响应的接口,在对应方法中设置组件值。

通过@Profile注解标识bean注册的环境。


Spring注解开发文章汇总

spring注解开发1——组件注册

spring注解开发2——bean的生命周期


spring注解开发3——属性赋值与自动装配
https://shgang97.github.io/posts/spring-annotation-3-40ec8cc4d1cc/
作者
shgang
发布于
2022年5月12日
更新于
2022年5月12日
许可协议