ImportBeanDefinitionRegistrar
大约 2 分钟源码解析bean的注入
应用场景
- 如果要实现
动态bean的装载
可以使用ImportBeanDefinitionRegistrar。尤其是如果想装载动态代理对象的时候,例如Mybatis的启动器就是使用了它实现了Mapper接口的代理对象装载的。 - 为什么不能使用
@Import
注解引入配置类装载动态代理对象呢?因为@Import
注解需要指定具体的配置类,然后再类中使用@Bean
返回注册对象。这个配置类我们也无法知道,new的bean对象也无法知道。 - 为什么不能使用
ImportSelector
装载动态代理对象呢?因为动态代理对象是运行时动态生成的,我们无法在编写代码的时候知道全类名。不同的类会生成不同的动态代理对象,无法把全类名提前写入到配置文件中。所以就不能使用ImportSelector
装载bean了。最根本的原因就是无法在编码阶段获得动态代理对象的类信息。
简单使用
Dog
类:
public class Dog {
private String name;
private int age;
public Dog() {
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
实现ImportBeanDefinitionRegistrar
接口:
public class SimpleRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 创建BeanDefinition
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName("com.registrar.Dog");
beanDefinition.setBeanClass(Dog.class);
ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
constructorArgumentValues.addIndexedArgumentValue(0, "大黄");
constructorArgumentValues.addIndexedArgumentValue(1, 2);
beanDefinition.setConstructorArgumentValues(constructorArgumentValues);
// 注册BeanDefinition
registry.registerBeanDefinition("dog", beanDefinition);
}
}
在Spring创建bean的源码探究中,我们发现bean是根据BeanDefinition
来创建的。在这里我们可以创建简单的BeanDefinition
对象,给Dog
对象传入了构造参数。
主启动类导入SimpleRegistrar
:
@SpringBootApplication
@Import(SimpleRegistrar.class)
public class SpringBootAnalysisApp {
private static final Logger logger = LoggerFactory.getLogger(SpringBootAnalysisApp.class);
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(SpringBootAnalysisApp.class, args);
Dog dog = applicationContext.getBean(Dog.class);
logger.info("dog: {}", dog);
}
}
结果:
com.sanfen.SpringBootAnalysisApp : dog: Dog{name='大黄', age=2}
发现我们在Spring容器中获取到了Dog对象,并且就是我们传入的构造属性。
分析一下:
- 在这里我们无须知道
Dog
的全类名,是通过构造BeanDefinition
对象来创建bean的。 - 如果是动态代理对象,我们只需要构建
BeanDefinition
对象,就可以把对象注册到容器中了。使用的时候直接利用Java的多态,使用接口类型的变量指向代理对象,就可以获取到容器中的动态生成的代理对象了。