• springboot 中 mybatis configuration 配置失效问题
  • 环境
  • 场景
  • springboot角度分析
    • SqlSessionFactory 设置Configuration
    • MybatisProperties从配置文件中设置Configuration
  • spring角度分析
    • MapperProxy之旅
    • SqlSessionFactoryBean之旅
    • 对比
  • 感谢

springboot 中 mybatis configuration 配置失效问题

环境

  1. springboot 2.0.0
  2. 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 的创建的地方

  1. 按照 MapperProxy->SqlSession(实现类SqlSessionTemplate)->sqlSessionFactory(实现类DefaultSqlSessionFactory)->configuration(实现类Configuration) 的顺序
    查看 Configuration 是否被设置
  2. 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")

  1. factoryBeanName: tk.mybatis.mapper.autoconfigure.MapperAutoConfiguration
  2. factoryMethodName: sqlSessionFactory

也就是说默认的定义在MapperAutoConfiguration.sqlSessionFactory()方法定义的sqlSessionFactory.

这样就从 spring 的角度跟进到了 springboot 的角度。 从而得知问题的根本原因

感谢

感谢问题提供者: 名友

springboot 中 mybatis configuration 配置失效问题相关推荐

  1. springboot 项目输出 sql 到控制台、 SpringBoot 中 Mybatis 打印 sql

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. SpringBoot中Mybatis打印sql 如果使用的是 application.propert ...

  2. SpringBoot中Logback常用配置以及自定义输出到MySql数据库

    之前基于SpringBoot开发的项目运行一段时间后,客户使用网站偶尔会出现接口调用失败的情况,每次产品经理询问是怎么回事的时候,都需要让运维提下最近的日志才能分析具体原因,这样时效性和便利性不能满足 ...

  3. springboot中druid数据源配置无效的问题和jar包找不到问题

    springboot中druid数据源配置无效的问题 阿里云的仓库 链接: 阿里云仓库. 自己在springboot项目中,引入druid的依赖,希望引入druid数据源. 但是idea中,虽然在这个 ...

  4. Springboot 中 Mybatis 的使用

    2019独角兽企业重金招聘Python工程师标准>>> 官方文档: Mybatis开发团队为Spring Boot 提供了 MyBatis-Spring-Boot-Starter 方 ...

  5. SpringBoot程序排除@Configuration配置类

    当我们的SpringBoot引用了别的SpringBoot项目并且不能修改其中的代码 但是不想使用其中某个@Configuration配置类中配置 想但单独排除某个配置类 可以在启动类加上 @Spri ...

  6. mybatis mapperLocations配置失效

    xml文件的目录结构:分oracle数据库的mapper.xml文件夹和瀚高数据库的mapper.xml文件夹 mybatis配置: 加载瀚高的xml文件 启动项目报错信息: 09:00:26.392 ...

  7. SpringBoot中mybatis配置多数据源

    首先需要创建多个数据库 简单的user表 CREATE TABLE `user` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAU ...

  8. SpringBoot中自定义日志配置logback-spring.xml

    场景 通过系统属性和传统的SpringBoot外部配置文件依然可以很好的支持日志控制和管理. 不同的日志系统对应的命名规范 Logback:logback-spring.xml  logbak-spr ...

  9. SpringBoot中Mybatis打印sql日志

    application.yml中加上 # springBoot+Mybatis 控制台打印SQL语句 (方式一) logging:level:com.zoctan.api.mapper : debug ...

最新文章

  1. 9个基于Java的搜索引擎框架
  2. Shell随机重命名所有当前目录一级子目录
  3. iOS 7.1 arm64 编辑报错 警告解决办法
  4. stream流把list转为map
  5. java实现递归层次遍历_Java实现二叉树的前序、中序、后序、层序遍历(递归方法)...
  6. 华为(英国)招聘CPU/GPU架构及系统软件工程师
  7. 【JavaScript】jsonp
  8. jQuery→事件、jQuery事件对象属性方法、多事件、自定义事件
  9. UnityShader33:GPU 实例化
  10. WWW'22 | 信息检索方向值得一读的3篇论文详解
  11. 《剑指offer》面试题60——把二叉树打印成多行(C++)
  12. Spring boot 日志 Logback
  13. ACM程序设计竞赛开幕式致辞
  14. 微信小程序mpvue框架
  15. AtCoder Beginner Contest 224题解 A-G
  16. linux下的orre命令,鳥哥的 Linux 私房菜
  17. 一文搞懂Oracle字符集
  18. Windows Server 2016搭建文件服务器
  19. node.js 从入门到?
  20. MySQL修改自增字段的自增值

热门文章

  1. EPUB和PDF的区别,有什么好用的IOS手机epub阅读器
  2. CSUST 1024 画画 题解(计算几何)
  3. Andrew Ng机器学习算法入门(二):机器学习分类
  4. XnView 1.97.0
  5. ps打开出现dll文件丢失怎么办,dll修复的三个方法
  6. ajaxsetup获取ajax的url_ajaxsetup,组合拦截器处理session过期,跳转登录页面
  7. mysql io次数_MySQL_揭秘SQL优化技巧 改善数据库性能,优化目标   1、减少 IO 次数 - phpStudy...
  8. 一杯白酒搅动的资本江湖
  9. 基于FPGA的数码管动态扫描显示(含代码)
  10. 数据库管理利器--Navicat Premium v12.1破解版