整合逻辑如下:
- 利用springboot主动拆卸的个性,应用MybatisAutoConfiguration开启mybatis和springboot的整合
-
SqlSessionFactory创立前,尝试读取mybatis前缀的配置文件(如mybatis-spring.xml),记录到SqlSessionFactoryBean#configLocation;
- 如果未读取到配置文件,间接采纳默认配置创立configuration
- 如果读取到配置文件,采纳配置信息创立configuration
-
通过FactoryBean形式创立SqlSessionFactory
- 默认事务工厂应用SpringManagedTransactionFactory,将来会创立SpringManagedTransaction
- 创立SqlSessionTemplate操作SqlSession,实质是做了一层代理,目标在于动静的开启和敞开SqlSession——这么做的起因是SqlSession是非线程平安的
- 在MybatisAutoConfiguration中,通过Scaner扫描@Mapper注解润饰的类,记入beanDefinitionMap中
以下通过源码察看具体实现。
一、主动拆卸
因为springboot主动拆卸的个性,找到mybatis-spring-boot-autoconfigure.jar下的配置文件:
META-INF
|__ spring.factories
文件内容:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
EnableAutoConfiguration指向了MybatisAutoConfiguration,所以要害配置肯定在这个类。
properties配置
// 导入properties配置
@EnableConfigurationProperties({MybatisProperties.class})
// 导入数据源配置
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
进入MybatisProperties类,发现了很多相熟的属性名(typeAliases、typeHandler等)
@ConfigurationProperties( prefix = "mybatis" )
public class MybatisProperties {
// 配置文件前缀,比方mybatis-spring.xml
public static final String MYBATIS_PREFIX = "mybatis";
// mapper地址
private String[] mapperLocations;
// typeAliases类型别名包门路
private String typeAliasesPackage;
private Class<?> typeAliasesSuperType;
// typeHandlers类型转换包门路
private String typeHandlersPackage;
private boolean checkConfigLocation = false;
// 执行器枚举:SIMPLE, REUSE, BATCH
private ExecutorType executorType;
二、SqlSessionFactory创立
回忆mybatis独自应用时的步骤:configuration->SqlSessionFactory->sqlSession->getMapper
来看看与springboot整合后的流程。
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
// 通过FactoryBean的形式创立SqlSessionFactory
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
// -- 如果指定了配置文件,则设置配置文件resource
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(
this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
// -- 如果用户未指定配置文件,则采纳局部默认配置创立configuration
this.applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
return factory.getObject();
}
spring与三方整合时,多采纳FactoryBean的形式,比方这里的SqlSessionFactoryBean。
察看它的getObject()办法
org.mybatis.spring.SqlSessionFactoryBean#getObject
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
// 间接在afterPropertiesSet里创立了sqlSessionFactory
this.afterPropertiesSet();
}
return this.sqlSessionFactory;
}
追踪afterPropertiesSet办法
org.mybatis.spring.SqlSessionFactoryBean#afterPropertiesSet
org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
XMLConfigBuilder xmlConfigBuilder = null;
Configuration targetConfiguration;
// -- configuration已创立
if (this.configuration != null) {
targetConfiguration = this.configuration;
}
// -- configuration未创立,但有配置文件地址:XPath形式解析配置文件
else if (this.configLocation != null) {
xmlConfigBuilder
= new XMLConfigBuilder(this.configLocation.getInputStream(),
(String)null, this.configurationProperties);
targetConfiguration = xmlConfigBuilder.getConfiguration();
}
// -- 采纳默认配置创立configuration
else {
targetConfiguration = new Configuration();
}
// -------- 略过typeAliases、typeHandler等设置 ---------
targetConfiguration.setEnvironment(
// 初始化Environment
new Environment(this.environment,
(TransactionFactory)(this.transactionFactory == null ?
// ### 默认采纳SpringManagedTransactionFactory治理事务
new SpringManagedTransactionFactory() :
this.transactionFactory),
this.dataSource));
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
SqlSessionFactory创立过程,与mybatis独自应用时简直完全一致;惟一的差异在于默认设置SpringManagedTransactionFactory治理事务(mybatis默认采纳jdbc形式治理事务,行将事务管制交给DB)
三、SqlSessionTemplate
接下来看看SqlSession的解决
MybatisAutoConfiguration里没有SqlSession只有SqlSessionTemplate
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
return executorType != null ?
new SqlSessionTemplate(sqlSessionFactory, executorType)
// 通过构造函数创立
: new SqlSessionTemplate(sqlSessionFactory);
}
看看构造函数做了什么。
org.mybatis.spring.SqlSessionTemplate#SqlSessionTemplate
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory,
ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy =
// 创立sqlsession代理
(SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
// ## invocationHandler
new SqlSessionTemplate.SqlSessionInterceptor());
}
间接查看代理的invocationHandler的invoke()
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1.sqlSession创立,外部实现:session = sessionFactory.openSession(executorType)
SqlSession sqlSession
= SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
Object unwrapped;
try {
// 2.原办法执行
Object result = method.invoke(sqlSession, args);
unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
throw (Throwable)unwrapped;
} finally {
// 3.sqlSession敞开
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
当SqlSessionTemplate中封装的一系列数据库操作方法(如下)被调用时,sqlSession会被主动创立和销毁
public <T> T selectOne(String statement) {
return this.sqlSessionProxy.selectOne(statement);
}
public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
}
public int update(String statement) {
return this.sqlSessionProxy.update(statement);
}
Spring应用SqlSessionTemplate代替mybatis的SqlSession,次要为了主动创立和销毁SqlSession。
因为SqlSession是线程不平安的类,不能复用。
四、Mapper创立
SqlSessionTemplate类中提供的一系列办法中,也包含getMapper()办法:
public <T> T getMapper(Class<T> type) {
return this.getConfiguration().getMapper(type, this);
}
这仿佛就和mybatis的应用形式接轨了。但认真一想又感觉不对,在工作中咱们获取mapper并不是通过这种形式。
spring环境下mapper的真正打开方式是这样才对:
@Autowire
UserMapper userMapper;
翻译一下就是通过上下文获取,即:context.getMapper(Class clz);
springboot是怎么主动拆卸Mapper的?
答案仍然在MybatisAutoConfiguration中。
@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean
追踪导入的MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
// 其实这行日志曾经解释的很分明了
MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
// ### 扫描器:扫描packages下所有被@Mapper润饰或接口
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
scanner.setAnnotationClass(Mapper.class);
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(packages));
}
察看scanner.doScan办法:
org.mybatis.spring.mapper.ClassPathMapperScanner#doScan
org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
org.springframework.context.annotation.ClassPathBeanDefinitionScanner#registerBeanDefinition
org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition
org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
// DefaultListableBeanFactory的属性,外面寄存了待初始化的“对象定义”,初始化后的对象都将放入spring上下文中
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);
// ————— 以上为属性 ——————
// 最终放到了beanDefinitionMap
this.beanDefinitionMap.put(beanName, beanDefinition);
- 在SqlSessionFactory创立环节,通过解析mapper.xml,将xml中指定的mapper转换成了代理对象(mybatis逻辑)
- 在MybatisAutoConfiguration中,又通过Scaner扫描@Mapper注解润饰的类,实现注入(spring逻辑)
附录
P6-P7常识合辑
发表回复