背景
这是我在2019年7月15日在 mybatis-plus
提的一个issue,当初也不是非得用 mybatis-plus
,所以也没有研究为什么,这里补充一下原因。
结论: 自定义AutoConfiguration的装配顺序优先级较低导致注入sqlSessionFactory失败。
补充下人话,Mybatis-plus装配的时候生成DataSource的Configuration还没有运行,所以没有进入
首先,我们有一个自动装配datasource的starter,然后由于MybatisPlusAutoConfiguration 上的ConditionalOnSingleCandidate注解导致未进行自动装配,最终导致了 Property ‘sqlSessionFactory’ or ‘sqlSessionTemplate’ are required 的错误。
这个是带上了这个 ConditionalOnSingleCandidate(DataSource.class),然后去掉该注解,正常运行。
重现步骤
druid + datasource 的starter
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@ConfigurationProperties(prefix = "xxxx.datasource")
@Data
public class DatasourceAutoConfigure {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private String username;
private String password;
private String url;
private String xy;
private String driverClassName;
private String type;
private Integer minIdle;
private Integer initialSize;
private Integer maxActive;
private Integer maxWait;
private String validationQuery;
private Boolean testWhileIdle;
private Boolean testOnBorrow;
private Boolean testOnReturn;
@Autowired(required = false)
private xxxxxxRequest xxxxxxRequest;
@Bean(initMethod = "init", destroyMethod = "close")
public DataSource dataSource() {
RayDruidDataSource druidDataSource = new RayDruidDataSource();
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
druidDataSource.setUrl(url);
if (StringUtils.hasText(xy)) {
logger.info("load xy:{}", xy);
druidDataSource.setXy(xy);
druidDataSource.setSecretRequest(secretRequest);
}
druidDataSource.setMinIdle(minIdle);
druidDataSource.setInitialSize(initialSize);
druidDataSource.setMaxActive(maxActive);
druidDataSource.setMaxWait(maxWait);
if (druidDataSource.getMaxWait() > -1) {
druidDataSource.setUseUnfairLock(true);
}
druidDataSource.setPoolPreparedStatements(false);
druidDataSource.setValidationQuery(validationQuery);
druidDataSource.setTestWhileIdle(testWhileIdle);
druidDataSource.setTestOnBorrow(testOnBorrow);
druidDataSource.setTestOnReturn(testOnReturn);
return druidDataSource;
}
}
报错信息
加上 @ConditionalOnSingleCandidate(DataSource.class)
导致 MybatisPlusAutoConfiguration
未被加载,最终引发报错。
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'registerInfoMapper' defined in file [/Users/chenshun/open/register-all/register-dao/target/classes/com/xxxxx/register/dao/mapper/RegisterInfoMapper.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:830)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
... 24 more
Caused by: java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
at org.springframework.util.Assert.notNull(Assert.java:198)
at org.mybatis.spring.support.SqlSessionDaoSupport.checkDaoConfig(SqlSessionDaoSupport.java:123)
at org.mybatis.spring.mapper.MapperFactoryBean.checkDaoConfig(MapperFactoryBean.java:73)
at org.springframework.dao.support.DaoSupport.afterPropertiesSet(DaoSupport.java:44)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774)
... 39 more
PS:不要使用h2.
我如何解决: 拷贝lib的MybatisPlusAutoConfiguration,移除掉该注解
然后 mybatis-plus
粘贴了 mybatis
的自动装配链接,表明这个类也是从 Mybatis
copy过来的。
问题结束。
遇到这个问题的人还挺多的。
实际原因
复现(稳定复现)
首先我用了 Mybatis
创建了一个 Demo
项目,运行正常。 随后我用 Mybatis-plus
替换了 Mybatis
的Starter(所有的配置都同步更新为Mybatis-plus),随后遭遇上述错误,根据网络上的大部分博客(博客结论是错误的),引入 mybatis-spring-boot-starter
,启动成功,但是使用 QueryWrapper
提示没有这个 statment
。
寻找原因
- 加入
mybatis-spring-boot-starter
依赖后启动成功,但是没有statment
说明没有正常加载Mybatis-plus
的配置 - 在
MybatisPlusAutoConfiguration
增加断点,Debug
启动 ,确定没有进来,自此结论确立MybatisPlusAutoConfiguration
没有加载, 而且是由于@ConditionalOnSingleCandidate(DataSource.class)
注解导致没有加载@ConditionalOnSingleCandidate(DataSource.class)
代表需要一个DataSource
,如果没有就不进行装配- 如果还不确定,可以开启
debug=true
,看一下装配结果
根据上述原因,查找可能的原因,找到原因如下,装配包名
- mybatis: org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
- me: io.github.chenshun00.datasource.DatasourceAutoConfigure
mybatis-pluse: com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
猜想是由于报名排序以后 c > i > o, 自动装配的优先级是mybatis-plus > me > mybatis , 这就可以解释为什么mybatis可以启动,mybatis-plus不能启动。
- 验证: 将
io.github.chenshun00.datasource.DatasourceAutoConfigure
更新为a.b.DatasourceAutoConfigure
发现可以启动成功,都符合预期。 说明猜想正确 - 启动不起来的原因是由于加载顺序不一致,那么只要将
DatasourceAutoConfigure
的优先级提高即可,@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
- 同时也理解了为什么Mybatis要这么写了
结论
SpringBoot 的自动装配如果没有被排除exclude掉,那么是会执行的,如果没有执行,说明装配条件不满足,于是问题就从报错现象转移到为什么starter的自动装配没有执行,根据装配现象可以查看是不是跟依赖的先后有联系。