说明
Mybatis的插件先于spring容器的完全初始化,虽然加了@Component会被扫描加入容器管理,但是此时Mybatis的拦截器lnterceptor注入的对象是还未初始化到容器的。
所以通过这种方式拿到的bean为空。
在最后分析为啥插件会先于spring容器完全初始化(不是指在容器之前初始化,是在容器加载完所有的BeanDefinition之前完成)
解决
使用@Lazy注解
既然先进行完全初始化,那么我们可以先不让其进行属性注入,当需要使用的时候再进行注入,这样就把注入时机延后了
采用@Lazy
的目的就是在第一次使用时才真正注入,加入容器时注入的是一个代理对象,因此可以解决此问题
1 2 3 4 5 6 7 8
| @Slf4j @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class MybatisDataRangeInterceptor implements Interceptor {
@Autowired @Lazy private AnnotationAuthHandler annotationAuthHandler; }
|
使用时从容器中拿
此方法同样的是将需要使用的Bean延后加载,我们可以直接注入一个ApplicationContext
,当第一次使用的时候再从ApplicationContext
中拿具体的Bean实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Slf4j @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class MybatisDataRangeInterceptor implements Interceptor,ApplicationContextAware {
private ApplicationContext applicationContext; private AnnotationAuthHandler annotationAuthHandler; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext=applicationContext; } public DataRangeDatabaseField getDataRangeDatabaseField() { return getAnnotationAuthHandler().getDataRangeDatabaseField(); } private synchronized AnnotationAuthHandler getAnnotationAuthHandler(){ if(annotationAuthHandler==null){ this.annotationAuthHandler = applicationContext.getBean(AnnotationAuthHandler.class); } return annotationAuthHandler; } }
|
Mybatis的插件先于spring容器的完全初始化原因
从springboot下mybatisplus的配置类文件可以看出端倪,或者自己实现了ApplicationContextAware之后打断点往前也可以分析到

最终定位到MybatisPlusAutoConfiguration
配置文件
他的构造器就需要传入ObjectProvider<Interceptor[]>
,我们知道构造器必须马上实例化,这不妥妥的要提前实例化了,看以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public MybatisPlusAutoConfiguration(MybatisPlusProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider, ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers, ObjectProvider<List<MybatisPlusPropertiesCustomizer>> mybatisPlusPropertiesCustomizerProvider, ApplicationContext applicationContext) { this.properties = properties; this.interceptors = interceptorsProvider.getIfAvailable(); this.typeHandlers = typeHandlersProvider.getIfAvailable(); this.languageDrivers = languageDriversProvider.getIfAvailable(); this.resourceLoader = resourceLoader; this.databaseIdProvider = databaseIdProvider.getIfAvailable(); this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable(); this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable(); this.mybatisPlusPropertiesCustomizers = mybatisPlusPropertiesCustomizerProvider.getIfAvailable(); this.applicationContext = applicationContext; }
|
由于 interceptorsProvider.getIfAvailable()
会马上注入Bean,会马上从容器中去拿。
但是我们需要注意,MybatisPlusAutoConfiguration
是一个配置类,也就是说它是通过注解扫描@Configuration
实现的。
如果我们配置Interceptor
@Bean注解的类在MybatisPlusAutoConfiguration
后执行,或者@ComponentScan
扫描在MybatisPlusAutoConfiguration
执行,则会出现Interceptor未被加载BeanDifinition,在初始化Bean的逻辑中则找到不到实例Bean,导致注入为空。
对于TypeHandler
、ConfigurationCustomizer
等同样有此问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Configuration public class AuthRabcConfig { @Autowired DataRangeDatabaseFieldProperties dataRangeDatabaseFieldProperties;
@Bean @ConditionalOnMissingBean(AnnotationAuthHandler.class) public AnnotationAuthHandler annotationAuthHandler(){ RabcAnnotationAuthHandler handler = new RabcAnnotationAuthHandler(); handler.setDataRangeDatabaseField(dataRangeDatabaseField()); handler.setSysAuthDataComposite(sysAuthDataComposite); handler.setSysAuthMenuComposite(sysAuthMenuComposite); return handler; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Bean @Configuration public class AuthCoreConfig {
@Autowired private AnnotationAuthHandler annotationAuthHandler;
ConfigurationCustomizer kwCustomizer(){ KwConfigurationCustomizer customizer = new KwConfigurationCustomizer(); return customizer.setAnnotationAuthHandler(annotationAuthHandler); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class KwConfigurationCustomizer implements ConfigurationCustomizer {
AnnotationAuthHandler annotationAuthHandler; @Override public void customize(MybatisConfiguration configuration) {
}
public KwConfigurationCustomizer setAnnotationAuthHandler(AnnotationAuthHandler annotationAuthHandler) { this.annotationAuthHandler = annotationAuthHandler; return this; } }
|