背景
这是我在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")@Datapublic 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 requiredat 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 moreCaused by: java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are requiredat 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的自动装配没有执行,根据装配现象可以查看是不是跟依赖的先后有联系。
