本文主要内容:
1. SpringBoot整合MyBatis主要步骤;
2. SpringBoot自动装配MyBatis源码分析;
1. SpringBoot整合MyBatis主要步骤
1. 引入数据库驱动(mysql为例)
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
2. 引入Mybatis starter
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
3. 增加数据源配置(Druid连接池可参考 Spring Boot数据源相关 )
spring: datasource: username: root password: 123456 url: jdbc:mysql:///test_mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC driver-class-name: com.mysql.cj.jdbc.Driver initialization-mode: always hikari: connection-init-sql: SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci connection-timeout: 30000 # 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 默认:30秒 minimum-idle: 5 # 最小连接数 maximum-pool-size: 20 # 最大连接数 auto-commit: true # 自动提交 idle-timeout: 600000 # 连接超时的最大时长(毫秒),超时则被释放(retired),默认:10分钟 pool-name: DateSourceHikariCP # 连接池名字 max-lifetime: 500000 # 连接的生命时长(毫秒),超时而且没被使用则被释放(retired),默认:30分钟 1800000ms connection-test-query: SELECT 1
4. 增加实体类 及 Mapper接口
public class User { private Integer id; private String username; private String password; private String birthday; ...... setter getter ...... }
public interface UserMapper { @Select("select * from user") List<User> findAll(); }
5. 启动类中增加Mapper接口扫描
@MapperScan(basePackages="com.kay.mapper") // com.kay.mapper根据自己项目自己修改
2. SpringBoot自动装配MyBatis源码分析
1. @MapperScan注解 本质是将MapperScannerRegistrar导入到容器中,在MapperScannerRegistrar中实现了ImportBeanDefinitionRegistrar接口类及registerBeanDefinitions方法,该方法将在spring实例化之前,调用invokeBeanFactoryPostProcessors 时被调用;
@Import({MapperScannerRegistrar.class}) public @interface MapperScan {
断点 MapperScannerRegistrar 下的 registerBeanDefinitions()方法,观看其调用栈可以验证其调用时机;
在 registerBeanDefinitions中,将
A. 调用doScan(),将指定包下basePackages下所有类信息,封装成BeanDefinition对象存档在IOC容器中,即beanDefinitionMap中;
B.调用processBeanDefinitions(),将mapper接口(GenericBeanDefinition)的beanClass属性为MapperFactoryBean类型并加入到spring的bean容器中;而MapperFactoryBean 实现了FactoryBean; 当后期spring实例化bean容器中对象时,针对于Mapper接口对应的BeanDefinition对象,将调用MapperFactoryBean的getBean()方法;返回 代理对象
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
....public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
......
}
2. 在mybatis-spring-boot-autoconfigure jar的META-INF/spring.factories中定义了
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {....
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
Configuration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new Configuration();
}
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
customizer.customize(configuration);
}
}
factory.setConfiguration(configuration);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}return factory.getObject();
}@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
此配置类MybatisAutoConfiguration将在容器调用BeanFactory的后置处理器invokeBeanFactoryPostProcessors时获取到类信息,封装成BeanDefinition对象存放到IOC容器beanDefinitionMap中,在finishBeanFactoryInitialization实例化对象时生成其注入的bean对象SqlSessionFactory 及 SqlSessionTemplate 对象;
在生成 SqlSessionFactory 时:在getObject()方法中, 将封装Configuration对象,最终调用this.sqlSessionFactoryBuilder.build(configuration)实例化SqlSessionFactory对象
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
}return this.sqlSessionFactory;
}
public void afterPropertiesSet() throws Exception {
.....
this.sqlSessionFactory = this.buildSqlSessionFactory();
}protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
XMLConfigBuilder xmlConfigBuilder = null;
Configuration configuration;
........
return this.sqlSessionFactoryBuilder.build(configuration);
}
在生成 SqlSessionTemplate 中:封装了SqlSession对象
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
Assert.notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
}
.......
}