说明

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之后打断点往前也可以分析到
e0f59b262de807152ea417c5544bc61b

最终定位到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(); // 实例化对应的Bean
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,导致注入为空。

对于TypeHandlerConfigurationCustomizer等同样有此问题

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;

/**
* 注解权限处理器
* 在引入了<kewen-auth-impl> 依赖的情况下配置
* 在系统中没有自定义AnnotationAuthHandler时配置
* @return
*/
@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;

//AuthRabcConfig 在MybatisPlusAutoConfiguration后执行,依然拿不到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;
}
}