上篇我们以mybatis如何注入mapper对象为引,发现mybatis使用了FactoryBean(动态代理)+动态注册beanDefinition 的方式实现了对多个bean进行注入。
这篇我们延续上篇的问题,什么是beanDefinition?动态注入beanDefinition是个什么样的过程?
1、beanDefinition
beanDefinition可以称之为spring bean的建模对象,
BeanDefintion{
//如果这个对象当中有值,spring会把他作为构造方法的参数填充
private ConstructorArgumentValues constructorArgumentValues;
//默认单例
private String scope = SCOPE_DEFAULT;
//是否需要依赖别的bean 默认没有
private String[] dependsOn;
//这个beanDefinition对象所描述的类
private volatile Object beanClass;
//如果这个对象当中有值,spring会把他作为setter方法的参数填充
private MutablePropertyValues propertyValues;
}
public class testAA implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition aa = beanFactory.getBeanDefinition("aa");
aa.setLazyInit(true);
aa.setFactoryBeanName("aaaa");
}
}
但是我们一般不使用这种方式。
spring中还提供了两种方式去注入BeanDefifinition。
2)、BeanDefifinitionRegistryPostProcessor接口
public class testAA implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("aa", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
可以实现该接口实现postProcessBeanDefinitionRegistry方法进行BeanDefition对象的注册
或者实现postProcessBeanFactory方法,获取到beanFactory对象编写类似1)进行修改BeanDefition。
我们还是不推荐第二种方式,比起这个还有很好的第三种方式
3)、ImportBeanDefifinitionRegistrar接口
(该接口在spring、springboot大量的使用,例如@Service、@Compnent等都是用它进行动态注册bean,很多三方框架集成Spring的时候,都会通过该接口,实现扫描指定的类,然后注册到spring容器中。 比如Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是通过该接口实现的自定义注册逻)
作用:可以动态注册Beandefition、bean对象等。
实现其registerBeanDefinitions方法,我们可以看看如何模拟第三方框架集成到spring中:
@Slf4j
public class testAA implements ImportBeanDefinitionRegistrar {
Map<String,BeanDefinition> map= new HashMap<>();
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//模拟第三方集成spring时,扫描需要注入的对象,并将对象转化为BeanDefinition类型存进map中
scan();
//遍历扫描到的BeanDefinition对象进行属性修改操作(修改非必须),然后进行动态注册
for(String key : map.keySet()){
AbstractBeanDefinition mapperBd = (AbstractBeanDefinition)
map.get(key);
Class mapperClz =mapperBd.getBeanClass();
System.out.println();
log.debug("before:{}",mapperBd.getBeanClass().getName());
mapperBd.setBeanClass(MyFactoryBean.class);
log.debug("after:{}",mapperBd.getBeanClass().getName());
mapperBd.getPropertyValues().add("mapperInterface",mapperClz.getName());
//动态注册BeanDefition
registry.registerBeanDefinition(key,mapperBd);
}
}
private void scan() {
//假装模拟了扫描过程,把扫描结果放到了map当中
}
}
然后编写配置类,将这个testAA配置类注入spring中(使用@Import注解)
@Configuration
@Import(testAA.class)
public class aaConfig{
}
(注意:上面的这几种方式都是动态注册Beanfinition,但是仍然需要将配置类注入到spring中,spring才能知道你注册了这个beandefinition)ImportBeanDefifinitionRegistrar一般搭配@Import一起使用Spring @Import导入Bean的三种类型:Component、ImportSelector、ImportBeanDefinitionRegistrar_每天都要加油呀!的博客-CSDN博客
一般第三方集成到spring时,第三方的框架是不能使用spring的注入注解的,都是在开发xxx-spring的集成插件的时候利用配置类使用@Import的方式去批量导入多个bean到spring中。
但是此时如果你用@Component注解将testAA注入spring中,此时则发现无法注入testAA中动态注册的Beandefinition,这是为什么???
registerBeanDefinitions方法的修改Beandefinition逻辑:
这也是@MapperScan将扫描的到Mapper动态注册为BeanDefinition的伪代码。(老版本的mybatis-spring和新版本的代码实现有点差别,但差别不大)
借助ImportBeanDefinitionRegistrar接口实现bean的动态注入 - 简书
java - 基于ImportBeanDefinitionRegistrar和FactoryBean动态注入Bean到Spring容器中 - 个人文章 - SegmentFault 思否
至此,我们知道了mybatis使用FactoryBean+ImportBeanDefifinitionRegistrar集成到spring中,将mapper对象注入到spring容器中。但这两篇文章的目的是了解对象如何注入spring的,但可以看到,我们对于扫描的部分并没有分析,下面我们开始了解spring是怎么扫描到那些标注了@Compnent注解的类,然后将他们注入到spring中的。(可以先做下上面几个链接的练习,熟悉下怎么模拟达到@Compnent的效果)