环境
mybatis-plus版本:3.3.1,对应mybatis版本:3.5.3,mybatis-spring版本:2.0.3
问题描述
由于在springboot项目启动时需要修改某个FactoryBean的BeanDefinition属性,于是向springboot容器中注入了BeanDefinitionRegistryPostProcessor实现类,实现了postProcessBeanDefinitionRegistry方法来修改BeanDefinition中的属性,但是怎么都不生效
问题分析
进入到springboot启动时AbstractApplicationContext
类的invokeBeanFactoryPostProcessors(beanFactory)
方法中,在这个方法中会进行容器中BeanDefinitionRegistryPostProcessor
的调用,发现mybatis启动时向容器中注入了MapperScannerConfigurer
,而在回调这个类的postProcessBeanDefinitionRegistry
方法中,有下面一段代码
Map<String, PropertyResourceConfigurer> prcs =
applicationContext.getBeansOfType(PropertyResourceConfigurer.class);
大家有兴趣可以自己debug看下,大致就是获取容器中的PropertyResourceConfigurer
对象,调用ApplicationContext
的getBeansOfType方法获取对应类型的bean时,它会首先获取所有的BeanDefinition,在首次获取时将原始的beanfefinition包装成RootBeanDefinition
放入到mergedBeanDefinitions缓存中,然后挨个解析BeanDefinition对应的class,但是有一个BeanDefinition比较特殊,就是FactoryBean,由于前面的applicationContext.getBeansOfType(PropertyResourceConfigurer.class
默认调用getBeansOfType(type, true, true);
,意思就是如果解析不出beandefinition,那么就初始化后来解析。
解析Factorybean对应BeanDefinition中的class
由于上面指定了可以进行初始化,执行关键代码
if (allowInit) {
FactoryBean<?> factoryBean = (mbd.isSingleton() ?
getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));
if (factoryBean != null) {
// Try to obtain the FactoryBean's object type from this early stage of the instance.
Class<?> type = getTypeForFactoryBean(factoryBean);
if (type != null) {
return ResolvableType.forClass(type);
}
// No type found for shortcut FactoryBean instance:
// fall back to full creation of the FactoryBean instance.
return super.getTypeForFactoryBean(beanName, mbd, true);
}
}
- 获取BeanDefinition中的
getDecoratedDefinition
,看是否对应的BeanDefinition能获取到对应的class - 如果上面的没有获取到,那么实例化这个FactoryBean,然后调用
getObjectType
, - 如果还是不能解析到对应的class,那么springboot容器就是创建这个bean,然后再调用
getObjectType
方法,而这时创建的这个bean的BeanDefinition是从mergedBeanDefinitions
,它从第一次在invokeBeanFactoryPostProcessors
后就不会改变了,直到invokeBeanFactoryPostProcessors
方法调用结束
总结前面产生的原因
在解析Factorybean对应的class时,如果满足下面两个条件:
mergedBeanDefinitions
中缓存的BeanDefinition没被修改- 在调用applicationContext的
getBeansOfType
方法时,指定了allowEagerInit
为true
那么springboot有可能会把这个bean提前创建好放入springboot容器中去,所以我们在BeanDefinitionRegistryPostProcessor
中的修改就不起作用了!!!
解决
两种办法:
- 第一种方法:替换mybatis-spring版本为2.0.5,笔者注意到在mybatis后续版本中已经解决了这个问题(也许是bug)
- 第二种方法包含下面几步:
- 保留现在出问题的mybatis-spring版本
BeanDefinitionRegistryPostProcessor
的实现类,也要实现PriorityOrdered
或者Ordered
类。- 接着,在
BeanDefinitionRegistryPostProcessor
实现类的postProcessBeanDefinitionRegistry
方法中,在修改BeanDefinition属性之前先调用BeanDefinitionRegistry
(其实就是DefaultListableBeanFactory
)的clearMetadataCache
方法,清除mergedBeanDefinitions
中的缓存
写在最后
如果对你有帮助,请给个赞吧。