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: WARN
org.mybatis: DEBUG
# 省略数据源相关配置...
自定义 SqlSessionFactory
@Configuration
public class MySqlConfig {
@Bean
public 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
@ConditionalOnMissingBean
public 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是什么呢? 怎么注入的呢?
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {
// 版面原因,省略了无关的属性设置
public static final String MYBATIS_PREFIX = "mybatis";
@NestedConfigurationProperty
private 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 . 就可以解决本文的问题
例如
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource, MybatisProperties mybatisProperties) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
//注入MybatisProperties,设置Configuration
sqlSessionFactoryBean.setConfiguration(mybatisProperties.getConfiguration());
return sqlSessionFactoryBean.getObject();
}
但是不推荐这样做. 因为 SqlSessionFactoryBean 还有一些其他的属性都要自己对应。
推荐做法: 用了tk.mybatis 就不要自己定义 SqlSessionFactory 了
阅读spring角度分析时,假设你已经了解 spring bean加载的相关知识
既然是Configuration的加载属性问题. 先去 MapperProxy 的创建的地方
接下来按照以下顺序寻找Configuration失效的根源
MapperProxy
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
说明确实没有被赋值成功
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 获得 sqlSessionFactory
this.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")
也就是说默认的定义在MapperAutoConfiguration.sqlSessionFactory()方法定义的sqlSessionFactory.
这样就从 spring 的角度跟进到了 springboot 的角度。 从而得知问题的根本原因
原文:https://blog.csdn.net/mz4138/article/details/81775137