springboot 中 mybatis configuration 配置失效问题
- springboot 中 mybatis configuration 配置失效问题
- 环境
- 场景
- springboot角度分析
- SqlSessionFactory 设置Configuration
- MybatisProperties从配置文件中设置Configuration
- spring角度分析
- MapperProxy之旅
- SqlSessionFactoryBean之旅
- 对比
- 感谢
springboot 中 mybatis configuration 配置失效问题
环境
- springboot 2.0.0
- tk.mybatis(mapper-spring-boot-starter) 2.0.0
场景
java 启动类
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import tk.mybatis.spring.annotation.MapperScan;@MapperScan(basePackages = "com.aya.service")
@SpringBootApplication
public class ProjectApplication {public static void main(String[] args) {new SpringApplicationBuilder(DesignServiceApplication.class).web(WebApplicationType.NONE).run(args);}
}
application.yml 配置
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:level:root: WARNorg.mybatis: DEBUG# 省略数据源相关配置...
自定义 SqlSessionFactory
@Configuration
public class MySqlConfig {@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);return sqlSessionFactoryBean.getObject();}
}
问题: mybatis.configuration 的所有配置无效,不打印mybatis相关的日志
解答:
产生问题的原因很简单,是因为tk.mybatis 当SqlSessionFactory
不存在定义的时候定义SqlSessionFactory
所以用tk.mybatis的时候,就不要手动注册 SqlSessionFactory
对象了
springboot角度分析
阅读springboot角度分析时,假设你已经了解 springboot 自动注入的相关知识
为什么 tk.mybatis 就可以注入,自定义就不可以呢?
接下来根据springboot的自动注入配置类,找到 MapperAutoConfiguration, 这里定义了 SqlSessionFactory
SqlSessionFactory 设置Configuration
tk.mybatis.mapper.autoconfigure.MapperAutoConfiguration
private final MybatisProperties properties;@Bean@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {// 版面原因,省略了无关的属性设置SqlSessionFactoryBean factory = new SqlSessionFactoryBean();Configuration configuration = this.properties.getConfiguration();if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {for (ConfigurationCustomizer customizer : this.configurationCustomizers) {customizer.customize(configuration);}}factory.setConfiguration(configuration);return factory;}
通过 SqlSessionFactory 设置configuration的地方发现, 是通过 MybatisProperties.getConfiguration() 获得的
那么MybatisProperties是什么呢? 怎么注入的呢?
MybatisProperties从配置文件中设置Configuration
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {// 版面原因,省略了无关的属性设置public static final String MYBATIS_PREFIX = "mybatis";@NestedConfigurationPropertyprivate Configuration configuration;
}
这个类表示:
当存在配置文件属性 mybatis.xx = 1 的时候,设置MybatisProperties属性为xx的值为1
这里就是一个嵌套的属性设置,mybatis.configuration.log-impl 就是设置MybatisProperties属性为configuration的属性为logImpl的值为
org.apache.ibatis.logging.stdout.StdOutImpl
当然tk.mybatis做了很多属性的映射,详细的设置请自己查看 MapperAutoConfiguration 的源码。
如果我们自定义 SqlSessionFactory 的时候,只要注入 MybatisProperties . 就可以解决本文的问题
例如
@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource, MybatisProperties mybatisProperties) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);//注入MybatisProperties,设置ConfigurationsqlSessionFactoryBean.setConfiguration(mybatisProperties.getConfiguration());return sqlSessionFactoryBean.getObject();}
但是不推荐这样做. 因为 SqlSessionFactoryBean 还有一些其他的属性都要自己对应。
推荐做法: 用了tk.mybatis 就不要自己定义 SqlSessionFactory 了
spring角度分析
阅读spring角度分析时,假设你已经了解 spring bean加载的相关知识
既然是Configuration的加载属性问题. 先去 MapperProxy 的创建的地方
- 按照 MapperProxy->SqlSession(实现类SqlSessionTemplate)->sqlSessionFactory(实现类DefaultSqlSessionFactory)->configuration(实现类Configuration) 的顺序
查看 Configuration 是否被设置 - SqlSessionFactoryBean.getObject 的地方,查看 Configuration 的来源
说明 MapperProxy 是所有的Mapper接口的动态代理执行方法类,实现了InvocationHandler,内部有sqlSession属性
MapperProxy之旅
接下来按照以下顺序寻找Configuration失效的根源
1. MapperProxy
2. SqlSessionFactoryBean::getObject
MapperProxy 是我们自定义 XXMapper 的代理类,实现了InvocationHandler接口
SqlSessionFactoryBean 是spring创建SqlSessionFactoryBean的过程
org.apache.ibatis.binding.MapperProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);}
这里断点后,查看 this.sqlSession.configuration.logImpl
的值是null
说明确实没有被赋值成功
SqlSessionFactoryBean之旅
org.mybatis.spring.SqlSessionFactoryBean
public SqlSessionFactory getObject() throws Exception {if (this.sqlSessionFactory == null) {afterPropertiesSet();}return this.sqlSessionFactory;}
SqlSessionFactoryBean 实现了 InitializingBean,FactoryBean。
所以创建的bean是通过getObject获得的,初始化方法是afterPropertiesSet
// 初始化 public void afterPropertiesSet() throws Exception {Assert.notNull(this.dataSource, "Property 'dataSource' is required");Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");// 通过 buildSqlSessionFactory 获得 sqlSessionFactorythis.sqlSessionFactory = this.buildSqlSessionFactory();}protected SqlSessionFactory buildSqlSessionFactory() throws IOException {XMLConfigBuilder xmlConfigBuilder = null;Configuration configuration;// this.configuration = null ,走最下面的configuration = new Configuration();// 也就是说 configuration 的属性必须在调用 afterPropertiesSet 之前就已经设置了if (this.configuration != null) {configuration = this.configuration;if (configuration.getVariables() == null) {configuration.setVariables(this.configurationProperties);} else if (this.configurationProperties != null) {configuration.getVariables().putAll(this.configurationProperties);}} else if (this.configLocation != null) {xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);configuration = xmlConfigBuilder.getConfiguration();} else {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Property `configuration` or 'configLocation' not specified, using default MyBatis Configuration");}configuration = new Configuration();configuration.setVariables(this.configurationProperties);}//版面原因,省略 configuration 设置属性的相关代码return this.sqlSessionFactoryBuilder.build(configuration);}
this.configuration = null ,走最下面的 configuration = new Configuration();
也就是说 configuration 的属性必须在调用 getObject 之前就已经设置了?
这不可能的,正常创建对象的过程中,都没填充属性,怎么可能在 getObject 设置 configuration 的属性呢
对比
打开 tk.mybatis 的官方测试项目或者新建一个测试项目,全部使用默认配置,
对spring的类 AbstractApplicationContext 的 refresh 下断点。
直到 invokeBeanFactoryPostProcessors 方法之后, 查看 ((DefaultListableBeanFactory) beanFactory).beanDefinitionMap.get("sqlSessionFactory")
- factoryBeanName: tk.mybatis.mapper.autoconfigure.MapperAutoConfiguration
- factoryMethodName: sqlSessionFactory
也就是说默认的定义在MapperAutoConfiguration.sqlSessionFactory()方法定义的sqlSessionFactory
.
这样就从 spring 的角度跟进到了 springboot 的角度。 从而得知问题的根本原因
感谢
感谢问题提供者: 名友
springboot 中 mybatis configuration 配置失效问题相关推荐
- springboot 项目输出 sql 到控制台、 SpringBoot 中 Mybatis 打印 sql
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. SpringBoot中Mybatis打印sql 如果使用的是 application.propert ...
- SpringBoot中Logback常用配置以及自定义输出到MySql数据库
之前基于SpringBoot开发的项目运行一段时间后,客户使用网站偶尔会出现接口调用失败的情况,每次产品经理询问是怎么回事的时候,都需要让运维提下最近的日志才能分析具体原因,这样时效性和便利性不能满足 ...
- springboot中druid数据源配置无效的问题和jar包找不到问题
springboot中druid数据源配置无效的问题 阿里云的仓库 链接: 阿里云仓库. 自己在springboot项目中,引入druid的依赖,希望引入druid数据源. 但是idea中,虽然在这个 ...
- Springboot 中 Mybatis 的使用
2019独角兽企业重金招聘Python工程师标准>>> 官方文档: Mybatis开发团队为Spring Boot 提供了 MyBatis-Spring-Boot-Starter 方 ...
- SpringBoot程序排除@Configuration配置类
当我们的SpringBoot引用了别的SpringBoot项目并且不能修改其中的代码 但是不想使用其中某个@Configuration配置类中配置 想但单独排除某个配置类 可以在启动类加上 @Spri ...
- mybatis mapperLocations配置失效
xml文件的目录结构:分oracle数据库的mapper.xml文件夹和瀚高数据库的mapper.xml文件夹 mybatis配置: 加载瀚高的xml文件 启动项目报错信息: 09:00:26.392 ...
- SpringBoot中mybatis配置多数据源
首先需要创建多个数据库 简单的user表 CREATE TABLE `user` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAU ...
- SpringBoot中自定义日志配置logback-spring.xml
场景 通过系统属性和传统的SpringBoot外部配置文件依然可以很好的支持日志控制和管理. 不同的日志系统对应的命名规范 Logback:logback-spring.xml logbak-spr ...
- SpringBoot中Mybatis打印sql日志
application.yml中加上 # springBoot+Mybatis 控制台打印SQL语句 (方式一) logging:level:com.zoctan.api.mapper : debug ...
最新文章
- 9个基于Java的搜索引擎框架
- Shell随机重命名所有当前目录一级子目录
- iOS 7.1 arm64 编辑报错 警告解决办法
- stream流把list转为map
- java实现递归层次遍历_Java实现二叉树的前序、中序、后序、层序遍历(递归方法)...
- 华为(英国)招聘CPU/GPU架构及系统软件工程师
- 【JavaScript】jsonp
- jQuery→事件、jQuery事件对象属性方法、多事件、自定义事件
- UnityShader33:GPU 实例化
- WWW'22 | 信息检索方向值得一读的3篇论文详解
- 《剑指offer》面试题60——把二叉树打印成多行(C++)
- Spring boot 日志 Logback
- ACM程序设计竞赛开幕式致辞
- 微信小程序mpvue框架
- AtCoder Beginner Contest 224题解 A-G
- linux下的orre命令,鳥哥的 Linux 私房菜
- 一文搞懂Oracle字符集
- Windows Server 2016搭建文件服务器
- node.js 从入门到?
- MySQL修改自增字段的自增值
热门文章
- EPUB和PDF的区别,有什么好用的IOS手机epub阅读器
- CSUST 1024 画画 题解(计算几何)
- Andrew Ng机器学习算法入门(二):机器学习分类
- XnView 1.97.0
- ps打开出现dll文件丢失怎么办,dll修复的三个方法
- ajaxsetup获取ajax的url_ajaxsetup,组合拦截器处理session过期,跳转登录页面
- mysql io次数_MySQL_揭秘SQL优化技巧 改善数据库性能,优化目标 1、减少 IO 次数 - phpStudy...
- 一杯白酒搅动的资本江湖
- 基于FPGA的数码管动态扫描显示(含代码)
- 数据库管理利器--Navicat Premium v12.1破解版