本文最后更新于:2022年5月12日 下午
摘要:介绍了Spring容器给属性赋值和自动装配
属性赋值
使用@Value注解
通过在需要赋值的属性上加@Value注解,然后设置注解的参数value完成对属性的赋值。参数value的值是一个字符串,字符串的引号内可以写:
- 基本数值:如基本的数据类型
- SpEL:#{}
- ${}:取出配置文件【properties】中的值(运行环境变量里面的值)
开发步骤
- 创建配置文件
在resources文件夹下创建一个properties文件,在文件夹中写要给属性赋的值
- 创建一个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;
@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 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;
@Configuration @PropertySource(value = {"classpath:/application.properties"}) public class MyConfig {
@Bean public Person person() { return new Person(); } }
|
- 查看输出结果
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;
public class PropertyTest { @Test public void testPropertySet() { ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); Person person = context.getBean(Person.class); System.out.println("person = " + person); } }
|
自动装配
Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值,主要有@Autowired、@Resource、@Inject三种方式。
标记属性完成自动装配
@Autowired自动注入原理
默认优先 按照类型去容器中找对应的组件:通过调用getBean(Class var1)方法
如果容器中有多个相同类型的组件,再将属性的名称作为组件的id去容器中查找:通过调用getBean(String var1)方法
还可以通过@Qualifier注解指定需要自动装配的组件id
自动装配一定要将属性赋值好,没有则会报错。可以通过@Autowired(required=false)设置容器中有依赖的bean则自动装配,没有则不装配。
还可以使用@Primary注解,来指定进行装配的首选bean。
开发步骤
- 分别创建三个类如下:
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;
@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;
@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;
@Controller @Data public class UserController { @Autowired private UserService userService1;
@Autowired @Qualifier(value = "userService") private UserService userService2;
}
|
- 新建一个配置类
给类中,也创建了一个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;
@Configuration @ComponentScan(value = {"com.shg.dao", "com.shg.service", "com.shg.controller"}) public class MyConfig1 {
@Bean public UserService userService1() { return new UserService(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;
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可以标记在构造器、参数、方法、属性位置,都是从容器中获取参数组件的值
- 标注在方法位置:@Bean+方法参数,参数从容器中获取
- 标注在都早起上
- 如果再见只有一个又惨构造器,这个有参构造器的@Autowired可以省略
- 放在参数位置
使用Spring容器底层的组件
自定义组件如果想用Spring容器底层的一些组件如:ApplicationContext、BeanFactory等,可以通过自定义组件实现xxxAware接口,在创建对象的时候,会调用接口规定的方法注入相关组件。
@Profile环境标识
Profile:Spring提供的可以根据当前环境,动态激活和切换一系列组件的功能
@Profile注解:指定组件在哪个环境的情况下才能被注册到容器中。
没有@Profile标识的bean,任何环境下都能注册这个bean
加了@Profile的bean,只有在标识的环境下才能注册到容器中,默认注册标识为“default”
如果@Profile写在类上,则只有指定环境的时候,整个配置类里面的所有配置才能生效
激活环境
- 使用命令行动态参数:在虚拟机参数位置设置-Dspring.profile.active=test/prod/dev
- 使用代码的方式激活某种环境
开发步骤——以配置数据库连接池为例
- 导入依赖
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 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 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;
@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 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); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Test public void testTestProfile() { 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); }
|
总结
- 属性赋值
通过@Value完成属性赋值,注解中参数value可以是一个字符串、也可以是SpEL表达式、还可以是${}。
- 自动装配
自动装配的注解有@Autowired、@Resource、@Inject,如果需要注入Spring底层的组件,如xxxAware,可以通过实现响应的接口,在对应方法中设置组件值。
通过@Profile注解标识bean注册的环境。
Spring注解开发文章汇总:
spring注解开发1——组件注册
spring注解开发2——bean的生命周期