Spring注解驱动 自动装配(三)
一、@Autowired自动装配基操
新建Person组件,并给name属性赋值为"张三",age属性赋值为30。
package com.example.demo.annotation;
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
@Component
public class Person {
public String name="张三";
public Integer age=30;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Person() {
}
}
测试单元,用@Autowired自动装配Person组件。
package com.example.demo;
import com.example.demo.annotation.MainConfig;
import com.example.demo.annotation.Person;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
class annotationTest {
@Autowired
Person person;
@Test
void test(){
System.err.println(person);
}
}
打印运行结果到控制台,发现这个自动装配可以生效。
我们接下来在配置类中也注册一个Person组件,赋值name属性为"李四",age属性为40以示区别。
package com.example.demo.annotation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = "com.example.demo.annotation")
public class MainConfig {
@Bean
public Person person111(){
return new Person("李四",40);
}
}
这样容器中就有两个同类型的组件了,一个id为类名首字母小写person,另一个为bean方法名person111。
下面在单元测试中看看是也不是?
@Test
void contextLoads() {
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfig.class);
//按Person类型获取组件
String[] typeNames=applicationContext.getBeanNamesForType(Person.class);
for (String typeName:typeNames) {
System.err.println(typeName);
}
}
打印运行结果出来看看:
可以看到IOC容器中现在有两个Person类型的组件。
再来运行单元测试,看看结果:
可以看到虽然现在容器中有两个Person类型的组件,但是默认装配的是 id 为 person 的组件。
那么要想装配 id 为 person111 的组件怎么办呢?
二、多个同类型组件的自动装配
1、使用@Primary注解
直接在bean组件的注册位置加上@Primary注解
@Bean
@Primary
public Person person111(){
return new Person("李四",40);
}
运行单元测试,打印结果如下:
由结果发现,标注了@Primary的bean组件会被优先装配。
2、修改装配名字
注释掉刚才加的@Primary注解,修改自动装配属性的名字:
@Autowired
Person person111;
然后修改单元测试并运行
@Test
void test(){
System.err.println(person111);
}
运行结果发现现在生效的也是 id 为 person111 的组件:
3、使用@Qualifier注解
用@Qualifier注解的value属性直接指定要装配的组件id。现在指定装配的组件id为person。
@Autowired
@Qualifier(value = "person")
Person person111;
再次运行单元测试,发现确实又装配了id为person的组件:
除了@Autowired注解以外,@Resource、@Inject注解也可以用来进行自动装配。
但是@Resource功能不够强大,@Inject需要导入额外的包,在Spring框架下,我们的首选仍然是@Autowired。
另外@Resource和@Inject在离开了Spring框架后仍然能够正常使用,而@Autowired目前只在Spring框架中生效。
三、@Profile根据环境自动装配
有时候我们需要根据不同的环境注册不同的组件,假定现在有dev开发、test测试和prod生产三个环境。
要怎么实现不同的环境装配不同的组件呢?@Profile注解可以帮您办到。
配置类,给配置类中多注册几个bean:
package com.example.demo.annotation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfig {
@Bean
public Person person111(){
return new Person("李四",40);
}
@Bean
public Person person222(){
return new Person("张三",30);
}
@Bean
public Person person333(){
return new Person("王五",50);
}
}
测试单元:
package com.example.demo;
import com.example.demo.annotation.MainConfig;
import com.example.demo.annotation.Person;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
class annotationTest {
@Test
void contextLoads() {
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfig.class);
String[] typeNames=applicationContext.getBeanNamesForType(Person.class);
for (String typeName:typeNames) {
System.err.println(typeName);
}
}
}
运行测试单元看看IOC容器中这些组件
果然都是刚才注册的bean组件。现在来给它们都标上@Profile注解,并分别设置为prod、test、dev环境。
@Profile("prod")
@Bean
public Person person111(){
return new Person("李四",40);
}
@Profile("test")
@Bean
public Person person222(){
return new Person("张三",30);
}
@Profile("dev")
@Bean
public Person person333(){
return new Person("王五",50);
}
运行单元测试,发现IOC容器中一个Person组件都没了
修改一下单元测试将环境设置为prod和test(可同时制定多种环境)。
@Test
void contextLoads() {
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext();
applicationContext.getEnvironment().setActiveProfiles("prod","test");
applicationContext.register(MainConfig.class);
applicationContext.refresh();
String[] typeNames=applicationContext.getBeanNamesForType(Person.class);
for (String typeName:typeNames) {
System.err.println(typeName);
}
}
再次运行单元测试,发现设为prod和test的组件都被导入进了IOC容器。
下面删掉设为dev的组件的@Profile注解,再次运行单元测试:
发现没有标注@Profile注解的bean默认是都要导入到容器中的。
另外这个注解也可以标注在类上,下面在配置类上标注@Profile注解并设为test环境。
package com.example.demo.annotation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Profile("test")
@Configuration
public class MainConfig {
@Bean
public Person person111(){
return new Person("李四",40);
}
@Bean
public Person person222(){
return new Person("张三",30);
}
@Bean
public Person person333(){
return new Person("王五",50);
}
}
这就表示只有在test环境下这些bean才会被导入到IOC容器中。
单元测试,设置激活环境为test:
@Test
void contextLoads() {
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext();
applicationContext.getEnvironment().setActiveProfiles("test");
applicationContext.register(MainConfig.class);
applicationContext.refresh();
String[] typeNames=applicationContext.getBeanNamesForType(Person.class);
for (String typeName:typeNames) {
System.err.println(typeName);
}
}
结果不出所料:
由于当前环境正是test,所以配置类中所有的bean组件都被导入到了IOC容器中。