怎么把想要的bean(而要注入的bean包括自己项目中的、第三方的)注册到spring容器,让spring替我们管理这些bean呢?
总结起来有四大方式:
- 一、通过包扫描特定注解的方式
- 二、@Import 注解
- 三、@Bean 注解
- 四、FactoryBean 接口
一、通过包扫描特定注解的方式
特定注解包括:@Controller、@Service、@Component、@Repository。
结合@ComponentScan注解配置相应的路径即可将需要的bean注入容器。
二、@Import注解
@Import注解注入bean有三种方式:
- @Import(要注入的bean的class对象),生成的bean的id默认是这个类的全类名
- @Import(ImportSelector实现类的class对象),导入的组件全类名数组
- @Import(ImportBeanDefinitionRegistrar实现类的class对象),手动注册需要的bean到容器
比如,我们要导入A、B、C、D、E几个类:
public class A {
}
public class B {
}
public class C {
}
public class D {
}
public class E {
}
2.1 @Import(要注入的bean的class对象)
@Configuration
@Import({A.class, B.class})
public class ImportTestConfig01 {
}
@SpringBootTest
public class ImportAnnotationTest {
@Test
public void test() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ImportTestConfig01.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
Stream.of(beanDefinitionNames).forEach(System.out::println);
}
}
importTestConfig01
fengge.config.importAnnotation.A
fengge.config.importAnnotation.B
可以看到A、B两个类已经注入容器,且id为全类名。
2.2 @Import(ImportSelector实现类的class对象)
public class MyImportSelector implements ImportSelector {
/**
* 方法返回值就是要导入的组件全类名数组
* AnnotationMetadata :@import注解所在类的所有注解信息
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Console.log("@import注解所在类的所有注解类型信息:" + importingClassMetadata.getAnnotationTypes());
return new String[]{"fengge.config.importAnnotation.C", "fengge.config.importAnnotation.D"};
}
}
@Configuration
@Import({A.class, B.class, MyImportSelector.class})
public class ImportTestConfig02 {
}
importTestConfig02
fengge.config.importAnnotation.A
fengge.config.importAnnotation.B
fengge.config.importAnnotation.C
fengge.config.importAnnotation.D
可以看到C、D两个类是通过 ImportSelector 接口的实现类 MyImportSelector 注入的。
AnnotationMetadata :@import注解所在类的所有注解信息。这里我们也打印了一下,正好就是 ImportTestConfig02 上面的两个注解:
@import注解所在类的所有注解类型信息:[org.springframework.context.annotation.Configuration, org.springframework.context.annotation.Import]
2.3 @Import(ImportBeanDefinitionRegistrar实现类的class对象)
public class MyRegistry implements ImportBeanDefinitionRegistrar {
/**
* 手动将需要的bean定义注册
* AnnotationMetadata :@import注解所在类的所有注解信息
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(E.class);
registry.registerBeanDefinition("E", rootBeanDefinition);
}
}
@Configuration
@Import({A.class, B.class, MyImportSelector.class, MyRegistry.class})
public class ImportTestConfig03 {
}
importTestConfig03
fengge.config.importAnnotation.A
fengge.config.importAnnotation.B
fengge.config.importAnnotation.C
fengge.config.importAnnotation.D
E
可以看到,E这个类型的bean是通过 ImportBeanDefinitionRegistrar 接口的实现类 MyRegistry 注入的。且id为类名,非全类名。
三、@Bean 注解
如果要注入的bean来自第三方,类没有使用@Controller、@Service、@Component、@Repository等注解去修饰,那就意味着spring扫描时就不会识别这是个要注入的bean,那就可以使用@bean注解去实现注入:
@Configuration // 相当于原来的xml文件,告诉spring这是个配置类
public class TestConfig {
@Bean("car") // 给spring注入一个bean,类型是返回值类型,id是默认是方法名
public CarDTO carDTO(){
return new CarDTO(1,"BMW");
}
}
假设这个CarDTO来自第三方,那就可以这样输入。
四、FactoryBean 接口
public class MyFactoryBean implements FactoryBean<CarDTO> {
/**
* 这里的CartDTO类型的bean组件,会注入spring容器
*/
@Override
public CarDTO getObject() throws Exception {
Console.log("MyFactoryBean中的getObject()方法生成carDTO这个bean");
return new CarDTO(666, "BMW");
}
/**
* 这里返回要注入的bean的Class对象
*/
@Override
public Class<?> getObjectType() {
return CarDTO.class;
}
/**
* 设置是否是单例
* 1、如果是true容器中只保留一个bean对象
* 2、若是false每次获取则产生新的bean对象
*/
@Override
public boolean isSingleton() {
return true;
}
}
@Configuration
public class BeanFactoryConfig {
@Bean
public MyFactoryBean myFactoryBean(){
Console.log("进入BeanFactoryConfig方法");
return new MyFactoryBean();
}
}
@Test
public void test1() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanFactoryConfig.class);
Console.log("容器已创建完成!");
Object myFactoryBean = applicationContext.getBean("myFactoryBean");
Console.log("实际生成的bean:" + myFactoryBean);
Object myFactoryBean01 = applicationContext.getBean("myFactoryBean");
Console.log("实际生成的bean:" + myFactoryBean01);
Console.log(myFactoryBean == myFactoryBean01);
}
进入BeanFactoryConfig方法
容器已创建完成!
MyFactoryBean中的getObject()方法生成carDTO这个bean
实际生成的bean:CarDTO(id=666, brand=BMW)
实际生成的bean:CarDTO(id=666, brand=BMW)
true
可以看到:
- 1、这里getBean("myFactoryBean"),是小写的,@Bean注解没有指定value时,默认方法名为id
- 2、@Bean注解是加在返回类型为MyFactoryBean类型的方法上的,但是getBean("myFactoryBean")时返回的bean并不是MyFactoryBean类型的组件,实际返回的是它里面的getObject()方法产生的CarDTO类型的bean
- 3、因为MyFactoryBean中的isSingleton()方法返回值为true,即单例模式,容器中只有一份实例,多次获取的是同一实例
若把MyFactoryBean中的 isSingleton()方法返回值设为false:
进入BeanFactoryConfig方法
容器已创建完成!
MyFactoryBean中的getObject()方法生成carDTO这个bean
实际生成的bean:CarDTO(id=666, brand=BMW)
MyFactoryBean中的getObject()方法生成carDTO这个bean
实际生成的bean:CarDTO(id=666, brand=BMW)
false
因为MyFactoryBean中的isSingleton()方法返回值为false,即多例模式,每次获取的是不同实例。
好了有了上面的说明,还怕你的bean不能注入吗!