该系列文档是本人在学习 Mybatis 的源码过程当中总结下来的,可能对读者不太友好,请结合个人源码注释(Mybatis源码分析 GitHub 地址、Mybatis-Spring 源码分析 GitHub 地址、Spring-Boot-Starter 源码分析 GitHub 地址)进行阅读html
MyBatis 版本:3.5.2java
MyBatis-Spring 版本:2.0.3git
MyBatis-Spring-Boot-Starter 版本:2.1.4github
在前面的一系列文档中对整个 MyBatis 框架进行了分析,相信你对 MyBatis 有了一个更加深刻的了解。在使用它的过程当中,须要本身建立 SqlSessionFactory 和 SqlSession,而后获取到 Mapper 接口的动态代理对象,执行数据库相关操做,对这些对象的管理并非那么简单。咱们一般会结合 Spring 来使用 MyBatis,将这些对象做为 Spring Bean 注入到 Spring 容器,也容许参与到 Spring 的事务管理之中spring
Spring 官方并无提供对 MyBatis3 的集成方案,因而在 MyBatis 社区将对 Spring 的集成做为一个 MyBatis 子项目 MyBatis-Spring,帮助你将 MyBatis 代码无缝地整合到 Spring 中,那么咱们一块儿来看看这个子项目是如何集成到 Spring 中的sql
在开始读这篇文档以前,须要对 Spring 有必定的了解,能够结合个人源码注释(Mybatis-Spring 源码分析 GitHub 地址)进行阅读,MyBatis-Spring官方文档数据库
主要涉及到的几个类:apache
org.mybatis.spring.SqlSessionFactoryBean
:实现 FactoryBean、InitializingBean、ApplicationListener 接口,负责构建一个 SqlSessionFactory 对象数组
org.mybatis.spring.mapper.MapperFactoryBean
:实现 FactoryBean 接口,继承 SqlSessionDaoSupport 抽象类,Mapper 接口对应 Spring Bean 对象,用于返回对应的动态代理对象缓存
org.mybatis.spring.support.SqlSessionDaoSupport
抽象类,继承了 DaoSupport 抽象类,用于构建一个 SqlSessionTemplate 对象
org.mybatis.spring.mapper.MapperScannerConfigurer
:实现了BeanDefinitionRegistryPostProcessor、InitializingBean接口,ApplicationContextAware、BeanNameAware接口,用于扫描Mapper接口,借助ClassPathMapperScanner
扫描器对Mapper接口的BeanDefinition对象(Spring Bean 的前身)进行修改
org.mybatis.spring.mapper.ClassPathMapperScanner
:继承了ClassPathBeanDefinitionScanner抽象类,负责执行扫描,修改扫描到的 Mapper 接口的 BeanDefinition 对象(Spring Bean的前身),将其 Bean Class 修改成 MapperFactoryBean,从而在 Spring 初始化该 Bean 的时候,会初始化成 MapperFactoryBean
类型,实现建立 Mapper 动态代理对象
org.mybatis.spring.annotation.MapperScannerRegistrar
:实现 ImportBeanDefinitionRegistrar、ResourceLoaderAware 接口
做为@MapperScann
注解的注册器,根据注解信息注册一个 MapperScannerConfigurer
对象,用于扫描 Mapper 接口
org.mybatis.spring.config.MapperScannerBeanDefinitionParser
:实现 BeanDefinitionParser 接口,<mybatis:scan />
的解析器,和MapperScannerRegistrar
的实现逻辑同样
org.mybatis.spring.SqlSessionTemplate
:实现 SqlSession 和 DisposableBean 接口,SqlSession 操做模板实现类,承担 SqlSessionFactory 和 SqlSession 的职责
org.mybatis.spring.SqlSessionUtils
:SqlSession 工具类,负责处理 MyBatis SqlSession 的生命周期,借助 Spring 的 TransactionSynchronizationManager 事务管理器管理 SqlSession 对像
大体逻辑以下:
MapperScannerConfigurer
的 Spring Bean,它会结合 ClassPathMapperScanner
扫描器,对指定包路径下的 Mapper 接口对应 BeanDefinition 对象(Spring Bean 的前身)进行修改,将其 Bean Class 修改成 MapperFactoryBean
类型,从而在 Spring 初始化该 Bean 的时候,会初始化成 MapperFactoryBean
对象,实现建立 Mapper 动态代理对象MapperFactoryBean
对象中getObject()
中,根据 SqlSessionTemplate
对象为该 Mapper 接口建立一个动态代理对象,也就是说在咱们注入该 Mapper 接口时,实际注入的是 Mapper 接口对应的动态代理对象SqlSessionTemplate
对象中,承担 SqlSessionFactory 和 SqlSession 的职责,结合 Spring 的事务体系进行处理<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${url}" /> <property name="driverClassName" value="${driver}" /> <property name="username" value="${username}" /> <property name="password" value="${password}" /> </bean> <!-- spring和MyBatis完美整合,不须要mybatis的配置映射文件 --> <bean id="mySqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- bean的名称为sqlSessionFactory会出现错误 --> <property name="dataSource" ref="dataSource" /> <!-- 引入配置文件 --> <property name="configLocation" value="classpath:mybatis-config.xml" /> <!-- 自动扫描mapping.xml文件 --> <property name="mapperLocations" value="classpath:com/fullmoon/study/mapping/*.xml" /> </bean> <!-- DAO接口所在包名,Spring会自动查找其下的类 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.fullmoon.study.dao" /> <property name="sqlSessionFactoryBeanName" value="mySqlSessionFactory" /> </bean>
DruidDataSource
数据源,SqlSessionFactoryBean
和MapperScannerConfigurer
对象org.mybatis.spring.SqlSessionFactoryBean
:实现 FactoryBean、InitializingBean、ApplicationListener 接口,负责构建一个 SqlSessionFactory 对象
关于Spring的FactoryBean
机制,不熟悉的先去了解一下,大体就是Spring在注入该类型的Bean时,调用的是它的getObject()
方法
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver(); private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory(); /** * 指定的 mybatis-config.xml 路径的资源 */ private Resource configLocation; private Configuration configuration; /** * 指定 XML 映射文件路径的资源数组 */ private Resource[] mapperLocations; /** * 数据源 */ private DataSource dataSource; private TransactionFactory transactionFactory; private Properties configurationProperties; private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); /** * SqlSession 工厂,默认为 DefaultSqlSessionFactory */ private SqlSessionFactory sqlSessionFactory; // EnvironmentAware requires spring 3.1 private String environment = SqlSessionFactoryBean.class.getSimpleName(); private boolean failFast; private Interceptor[] plugins; private TypeHandler<?>[] typeHandlers; private String typeHandlersPackage; @SuppressWarnings("rawtypes") private Class<? extends TypeHandler> defaultEnumTypeHandler; private Class<?>[] typeAliases; private String typeAliasesPackage; private Class<?> typeAliasesSuperType; private LanguageDriver[] scriptingLanguageDrivers; private Class<? extends LanguageDriver> defaultScriptingLanguageDriver; // issue #19. No default provider. private DatabaseIdProvider databaseIdProvider; private Class<? extends VFS> vfs; private Cache cache; private ObjectFactory objectFactory; private ObjectWrapperFactory objectWrapperFactory; }
能够看到上面定义的各类属性,这里就不一一解释了,根据名称能够知道属性的做用
afterPropertiesSet()
方法,实现的 InitializingBean 接口,在 Spring 容器中,初始化该 Bean 时,会调用该方法,方法以下:
@Override public void afterPropertiesSet() throws Exception { // 校验 dataSource 数据源不能为空 notNull(dataSource, "Property 'dataSource' is required"); // 校验 sqlSessionFactoryBuilder 构建器不能为空,上面默认 new 一个对象 notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); // configuration 和 configLocation 有且只有一个不为空 state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), "Property 'configuration' and 'configLocation' can not specified with together"); // 初始化 SqlSessionFactory this.sqlSessionFactory = buildSqlSessionFactory(); }
buildSqlSessionFactory()
方法,初始化 SqlSessionFactory
buildSqlSessionFactory()
方法,根据配置信息构建一个SqlSessionFactory
实例,方法以下:
protected SqlSessionFactory buildSqlSessionFactory() throws Exception { final Configuration targetConfiguration; // 初始化 Configuration 全局配置对象 XMLConfigBuilder xmlConfigBuilder = null; if (this.configuration != null) { // 若是已存在 Configuration 对象 targetConfiguration = this.configuration; if (targetConfiguration.getVariables() == null) { targetConfiguration.setVariables(this.configurationProperties); } else if (this.configurationProperties != null) { targetConfiguration.getVariables().putAll(this.configurationProperties); } } else if (this.configLocation != null) { // 不然,若是配置了 mybatis-config.xml 配置文件 xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); targetConfiguration = xmlConfigBuilder.getConfiguration(); } else { LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"); // 不然,建立一个 Configuration 对象 targetConfiguration = new Configuration(); Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables); } /* * 若是配置了 ObjectFactory(实例工厂)、ObjectWrapperFactory(ObjectWrapper工厂)、VFS(虚拟文件系统) * 则分别往 Configuration 全局配置对象设置 */ Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory); Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory); Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl); /* * 若是配置了须要设置别名的包路径,则扫描该包路径下的 Class 对象 * 往 Configuration 全局配置对象的 TypeAliasRegistry 别名注册表进行注册 */ if (hasLength(this.typeAliasesPackage)) { scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream() // 过滤掉匿名类 .filter(clazz -> !clazz.isAnonymousClass()) // 过滤掉接口 .filter(clazz -> !clazz.isInterface()) // 过滤掉内部类 .filter(clazz -> !clazz.isMemberClass()) .forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias); } /* * 若是单独配置了须要设置别名的 Class 对象 * 则将其往 Configuration 全局配置对象的 TypeAliasRegistry 别名注册表注册 */ if (!isEmpty(this.typeAliases)) { Stream.of(this.typeAliases).forEach(typeAlias -> { targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias); LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'"); }); } // 往 Configuration 全局配置对象添加 Interceptor 插件 if (!isEmpty(this.plugins)) { Stream.of(this.plugins).forEach(plugin -> { targetConfiguration.addInterceptor(plugin); LOGGER.debug(() -> "Registered plugin: '" + plugin + "'"); }); } // 扫描包路径,往 Configuration 全局配置对象添加 TypeHandler 类型处理器 if (hasLength(this.typeHandlersPackage)) { scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass()) .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers())) .forEach(targetConfiguration.getTypeHandlerRegistry()::register); } // 往 Configuration 全局配置对象添加 TypeHandler 类型处理器 if (!isEmpty(this.typeHandlers)) { Stream.of(this.typeHandlers).forEach(typeHandler -> { targetConfiguration.getTypeHandlerRegistry().register(typeHandler); LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'"); }); } // 设置默认的枚举类型处理器 targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler); // 往 Configuration 全局配置对象添加 LanguageDriver 语言驱动 if (!isEmpty(this.scriptingLanguageDrivers)) { Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> { targetConfiguration.getLanguageRegistry().register(languageDriver); LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'"); }); } // 设置默认的 LanguageDriver 语言驱动 Optional.ofNullable(this.defaultScriptingLanguageDriver) .ifPresent(targetConfiguration::setDefaultScriptingLanguage); // 设置当前数据源的数据库 id if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls try { targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); } catch (SQLException e) { throw new NestedIOException("Failed getting a databaseId", e); } } // 添加 Cache 缓存 Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache); if (xmlConfigBuilder != null) { try { // 若是配置了 mybatis-config.xml 配置文件,则初始化 MyBatis xmlConfigBuilder.parse(); LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'"); } catch (Exception ex) { throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex); } finally { ErrorContext.instance().reset(); } } // 设置 Environment 环境信息 targetConfiguration.setEnvironment(new Environment(this.environment, this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory, this.dataSource)); // 若是配置了 XML 映射文件的路径 if (this.mapperLocations != null) { if (this.mapperLocations.length == 0) { LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found."); } else { /* * 遍历全部的 XML 映射文件 */ for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments()); // 解析 XML 映射文件 xmlMapperBuilder.parse(); } catch (Exception e) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); } finally { ErrorContext.instance().reset(); } LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'"); } } } else { LOGGER.debug(() -> "Property 'mapperLocations' was not specified."); } // 经过构建器建立一个 DefaultSqlSessionFactory 对象 return this.sqlSessionFactoryBuilder.build(targetConfiguration); }
方法有点长,主要是经过配置信息建立一个 Configuration 对象,而后构建一个 DefaultSqlSessionFactory 对象
初始化 Configuration
全局配置对象
往 Configuration 对象中设置相关配置属性
若是是1.2
步生成的 Configuration 对象,那么调用 XMLConfigBuilder
的 parse()
方法进行解析,初始化 MyBatis,在《MyBatis 初始化(一)之加载mybatis-config.xml》中分析过
若是配置了 XML 映射文件的路径mapperLocations
,则进行遍历依次解析,经过建立XMLMapperBuilder
对象,调用其parse()
方法进行解析,在《MyBatis 初始化(二)之加载Mapper接口与映射文件》的XMLMapperBuilder小节中分析过
经过SqlSessionFactoryBuilder
构建器建立一个DefaultSqlSessionFactory
对象
在 MyBatis-Spring 项目中,除了经过 mybatis-config.xml 配置文件配置 Mapper 接口的方式之外,还提供了几种配置方法,这里的
SqlSessionFactoryBean.mapperLocations
也算一种这样在 Spring 中,Mapper 接口和对应的 XML 映射文件名称能够不一致,文件中里面配置
namepace
正确就能够了
getObject()
方法,在 Spring 容器中,注入当前 Bean 时调用该方法,也就是返回 DefaultSqlSessionFactory 对象,方法以下:
@Override public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { // 若是为空则初始化 sqlSessionFactory afterPropertiesSet(); } // 返回 DefaultSqlSessionFactory 对象 return this.sqlSessionFactory; }
onApplicationEvent(ApplicationEvent event)
方法,监听 ContextRefreshedEvent 事件,若是还存在未初始化完成的 MapperStatement 们,则再进行解析,方法以下:
@Override public void onApplicationEvent(ApplicationEvent event) { // 若是配置了须要快速失败,而且监听到了 Spring 容器初始化完成事件 if (failFast && event instanceof ContextRefreshedEvent) { // fail-fast -> check all statements are completed // 将 MyBatis 中还未彻底解析的对象,在这里再进行解析 this.sqlSessionFactory.getConfiguration().getMappedStatementNames(); } }
org.mybatis.spring.mapper.MapperFactoryBean
:实现 FactoryBean 接口,继承 SqlSessionDaoSupport 抽象类,Mapper 接口对应 Spring Bean 对象,用于返回对应的动态代理对象
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { /** * Mapper 接口 */ private Class<T> mapperInterface; /** * 是否添加到 {@link Configuration} 中,默认为 true */ private boolean addToConfig = true; public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } }
mapperInterface
:对应的Mapper接口
addToConfig
:是否添加到 Configuration 中,默认为 true
getObject()
方法,在 Spring 容器中,注入当前 Bean 时调用该方法,也就是返回 Mapper 接口对应的动态代理对象,方法以下:
@Override public T getObject() throws Exception { // getSqlSession() 方法返回 SqlSessionTemplate 对象 return getSqlSession().getMapper(this.mapperInterface); }
getSqlSession()
方法在SqlSessionDaoSupport
中定义,返回的是SqlSessionTemplate
对象,后续会讲到
能够先暂时理解为就是返回一个 DefaultSqlSession,获取mapperInterface
Mapper接口对应的动态代理对象
这也就是为何在Spring中注入Mapper接口Bean时,咱们能够直接调用它的方法
checkDaoConfig()
方法,校验该 Mapper 接口是否被初始化并添加到 Configuration 中
@Override protected void checkDaoConfig() { // 校验 sqlSessionTemplate 非空 super.checkDaoConfig(); // 校验 mapperInterface 非空 notNull(this.mapperInterface, "Property 'mapperInterface' is required"); /* * 若是该 Mapper 接口没有被解析至 Configuration,则对其进行解析 */ Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { // 将该 Mapper 接口添加至 Configuration,会对该接口进行一系列的解析 configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); } } }
sqlSessionTemplate
非空mapperInterface
非空由于继承了DaoSupport
抽象类,实现了 InitializingBean 接口,在 afterPropertiesSet() 方法中会调用checkDaoConfig()
方法
org.mybatis.spring.support.SqlSessionDaoSupport
抽象类,继承了 DaoSupport 抽象类,用于构建一个 SqlSessionTemplate 对象,代码以下:
public abstract class SqlSessionDaoSupport extends DaoSupport { private SqlSessionTemplate sqlSessionTemplate; public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) { this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory); } } @SuppressWarnings("WeakerAccess") protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } public final SqlSessionFactory getSqlSessionFactory() { return (this.sqlSessionTemplate != null ? this.sqlSessionTemplate.getSqlSessionFactory() : null); } public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSessionTemplate = sqlSessionTemplate; } public SqlSession getSqlSession() { return this.sqlSessionTemplate; } public SqlSessionTemplate getSqlSessionTemplate() { return this.sqlSessionTemplate; } @Override protected void checkDaoConfig() { notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required"); } }
setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)
方法,将 SqlSessionFactory 构建成咱们须要的 SqlSessionTemplate 对象,该对象在后续讲到checkDaoConfig()
方法,校验 SqlSessionTemplate 非空org.mybatis.spring.mapper.MapperScannerConfigurer
:实现了BeanDefinitionRegistryPostProcessor
、InitializingBean接口,ApplicationContextAware、BeanNameAware接口
用于扫描Mapper接口,借助ClassPathMapperScanner
修改Mapper接口的BeanDefinition对象(Spring Bean 的前身),将Bean的Class对象修改成MapperFactoryBean
类型
那么在Spring初始化该Bean的时候,会初始化成MapperFactoryBean类型
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { /** * Mapper 接口的包路径 */ private String basePackage; /** * 是否要将接口添加到 Configuration 全局配置对象中 */ private boolean addToConfig = true; private String lazyInitialization; private SqlSessionFactory sqlSessionFactory; private SqlSessionTemplate sqlSessionTemplate; private String sqlSessionFactoryBeanName; private String sqlSessionTemplateBeanName; private Class<? extends Annotation> annotationClass; private Class<?> markerInterface; private Class<? extends MapperFactoryBean> mapperFactoryBeanClass; private ApplicationContext applicationContext; private String beanName; private boolean processPropertyPlaceHolders; private BeanNameGenerator nameGenerator; private String defaultScope; @Override public void afterPropertiesSet() throws Exception { notNull(this.basePackage, "Property 'basePackage' is required"); } }
在SqlSessionFactoryBean小节的示例中能够看到,定义了basePackage
和sqlSessionFactoryBeanName
两个属性
basePackage
:Mapper 接口的包路径addToConfig
:是否要将接口添加到 Configuration 全局配置对象中sqlSessionFactoryBeanName
:SqlSessionFactory的Bean Name在afterPropertiesSet()
方法中会校验basePackage
非空
在 MyBatis-Spring 项目中,除了经过 mybatis-config.xml 配置文件配置 Mapper 接口的方式之外,还提供了几种配置方法,这里的配置
basePackage
属性也算一种这样在 Spring 中,Mapper 接口和对应的 XML 映射文件名称能够不一致,文件中里面配置
namepace
正确就能够了
postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
方法,在 BeanDefinitionRegistry 完成后进行一些处理
这里会借助ClassPathMapperScanner
扫描器,扫描指定包路径下的 Mapper 接口,方法以下:
@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { // 处理属性中的占位符 processPropertyPlaceHolders(); } // 建立一个 Bean 扫描器 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); // 是否要将 Mapper 接口添加到 Configuration 全局配置对象中 scanner.setAddToConfig(this.addToConfig); scanner.setAnnotationClass(this.annotationClass); scanner.setMarkerInterface(this.markerInterface); scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setSqlSessionTemplate(this.sqlSessionTemplate); // 设置 SqlSessionFactory 的 BeanName scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); scanner.setResourceLoader(this.applicationContext); scanner.setBeanNameGenerator(this.nameGenerator); scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass); if (StringUtils.hasText(lazyInitialization)) { scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization)); } if (StringUtils.hasText(defaultScope)) { scanner.setDefaultScope(defaultScope); } // 添加几个过滤器 scanner.registerFilters(); scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }
processPropertyPlaceHolders()
方法ClassPathMapperScanner
对象addToConfig
、sqlSessionFactoryBeanName
registerFilters()
方法,添加几个过滤器,过滤指定路径下的 Mapper 接口scan
方法,开始扫描 basePackage
路径下的 Mapper 接口org.mybatis.spring.mapper.ClassPathMapperScanner
:继承了ClassPathBeanDefinitionScanner抽象类
负责执行扫描,修改扫描到的 Mapper 接口的 BeanDefinition 对象(Spring Bean的前身),将其 Bean Class 修改成 MapperFactoryBean,从而在 Spring 初始化该 Bean 的时候,会初始化成 MapperFactoryBean
类型,实现建立 Mapper 动态代理对象
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { private static final Logger LOGGER = LoggerFactory.getLogger(ClassPathMapperScanner.class); /** * 是否要将 Mapper 接口添加到 Configuration 全局配置对象中 */ private boolean addToConfig = true; private boolean lazyInitialization; private SqlSessionFactory sqlSessionFactory; private SqlSessionTemplate sqlSessionTemplate; private String sqlSessionTemplateBeanName; /** * SqlSessionFactory Bean 的名称 */ private String sqlSessionFactoryBeanName; private Class<? extends Annotation> annotationClass; private Class<?> markerInterface; /** * 将 Mapper 接口转换成 MapperFactoryBean 对象 */ private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class; private String defaultScope; }
basePackage
:Mapper 接口的包路径addToConfig
:是否要将接口添加到 Configuration 全局配置对象中sqlSessionFactoryBeanName
:SqlSessionFactory的Bean Name上面这几个属性在 MapperScannerConfigurer 建立该对象的时候会进行赋值
registerFilters()
方法,添加几个过滤器,用于扫描 Mapper 接口的过程当中过滤出咱们须要的 Mapper 接口,方法以下:
public void registerFilters() { // 标记是否接受全部的 Mapper 接口 boolean acceptAllInterfaces = true; // if specified, use the given annotation and / or marker interface // 若是配置了注解,则扫描有该注解的 Mapper 接口 if (this.annotationClass != null) { addIncludeFilter(new AnnotationTypeFilter(this.annotationClass)); acceptAllInterfaces = false; } // override AssignableTypeFilter to ignore matches on the actual marker interface // 若是配置了某个接口,则也须要扫描该接口 if (this.markerInterface != null) { addIncludeFilter(new AssignableTypeFilter(this.markerInterface) { @Override protected boolean matchClassName(String className) { return false; } }); acceptAllInterfaces = false; } if (acceptAllInterfaces) { // default include filter that accepts all classes // 若是上面两个都没有配置,则接受全部的 Mapper 接口 addIncludeFilter((metadataReader, metadataReaderFactory) -> true); } // exclude package-info.java // 排除 package-info.java 文件 addExcludeFilter((metadataReader, metadataReaderFactory) -> { String className = metadataReader.getClassMetadata().getClassName(); return className.endsWith("package-info"); }); }
2
、3
步,则添加一个过滤器,接收全部的接口doScan(String... basePackages)
方法,扫描指定包路径,根据上面的过滤器,获取路径下对应的 BeanDefinition 集合,进行一些后置处理,方法以下:
@Override public Set<BeanDefinitionHolder> doScan(String... basePackages) { // 扫描指定包路径,根据上面的过滤器,获取到路径下 Class 对象的 BeanDefinition 对象 Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } else { // 后置处理这些 BeanDefinition processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
BeanDefinition
对象注册到 BeanDefinitionRegistry 注册表中,并返回processBeanDefinitions
方法,进行一些后置处理processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions)
方法,对指定包路径下符合条件的BeanDefinition
对象进行一些处理,修改其 Bean Class 为 MapperFactoryBean
类型,方法以下:
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { AbstractBeanDefinition definition; // <1> 获取 BeanDefinition 注册表,而后开始遍历 BeanDefinitionRegistry registry = getRegistry(); for (BeanDefinitionHolder holder : beanDefinitions) { definition = (AbstractBeanDefinition) holder.getBeanDefinition(); boolean scopedProxy = false; if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) { // 获取被装饰的 BeanDefinition 对象 definition = (AbstractBeanDefinition) Optional .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition()) .map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException( "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]")); scopedProxy = true; } // <2> 获取对应的 Bean 的 Class 名称,也就是 Mapper 接口的 Class 对象 String beanClassName = definition.getBeanClassName(); LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface"); // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean // <3> 往构造方法的参数列表中添加一个参数,为当前 Mapper 接口的 Class 对象 definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59 /* * <4> 修改该 Mapper 接口的 Class对象 为 MapperFactoryBean.class * 这样一来当你注入该 Mapper 接口的时候,实际注入的是 MapperFactoryBean 对象,构造方法的入参就是 Mapper 接口 */ definition.setBeanClass(this.mapperFactoryBeanClass); // <5> 添加 addToConfig 属性 definition.getPropertyValues().add("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false; // <6> 开始添加 sqlSessionFactory 或者 sqlSessionTemplate 属性 /* * 1. 若是设置了 sqlSessionFactoryBeanName,则添加 sqlSessionFactory 属性,实际上配置的是 SqlSessionFactoryBean 对象 * 2. 不然,若是配置了 sqlSessionFactory 对象,则添加 sqlSessionFactory 属性 */ if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionFactory != null) { definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true; } /* * 1. 若是配置了 sqlSessionTemplateBeanName,则添加 sqlSessionTemplate 属性 * 2. 不然,若是配置了 sqlSessionTemplate 对象,则添加 sqlSessionTemplate 属性 * SqlSessionFactory 和 SqlSessionTemplate 都配置了则会打印一个警告 */ if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { // 若是上面已经清楚的使用了 SqlSessionFactory,则打印一个警告 LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } // 添加 sqlSessionTemplate 属性 definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionTemplate != null) { if (explicitFactoryUsed) { LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } /* * 上面没有找到对应的 SqlSessionFactory,则设置经过类型注入 */ if (!explicitFactoryUsed) { LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } definition.setLazyInit(lazyInitialization); if (scopedProxy) { // 已经封装过的则直接执行下一个 continue; } if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) { definition.setScope(defaultScope); } /* * 若是不是单例模式,默认是 * 将 BeanDefinition 在封装一层进行注册 */ if (!definition.isSingleton()) { BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true); if (registry.containsBeanDefinition(proxyHolder.getBeanName())) { registry.removeBeanDefinition(proxyHolder.getBeanName()); } registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition()); } } }
获取 BeanDefinition 注册表,而后开始遍历
获取对应的 Bean 的 Class 名称,也就是 Mapper 接口的 Class 对象
往构造方法的参数列表中添加一个参数,为当前 Mapper 接口的名称,由于 MapperFactoryBean 的构造方法的入参就是 Mapper 接口
修改该 Mapper 接口的 Class 对象 为 MapperFactoryBean
,根据第3
步则会为该 Mapper 接口建立一个对应的 MapperFactoryBean
对象了
添加 addToConfig
属性,Mapper 是否添加到 Configuration 中
开始添加 sqlSessionFactory
或者 sqlSessionTemplate
属性
若是设置了 sqlSessionFactoryBeanName,则添加 sqlSessionFactory 属性,实际上配置的是 SqlSessionFactoryBean 对象,
不然,若是配置了 sqlSessionFactory 对象,则添加 sqlSessionFactory 属性
在 SqlSessionDaoSupport
的 setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)
方法中你会发现建立的就是SqlSessionTemplate
对象
若是配置了 sqlSessionTemplateBeanName,则添加 sqlSessionTemplate 属性
不然,若是配置了 sqlSessionTemplate 对象,则添加 sqlSessionTemplate 属性
上面没有找到对应的 SqlSessionFactory,则设置经过类型注入
该方法的处理逻辑大体如上描述,主要作了一下几个事:
将 Mapper 接口的 BeanDefinition 对象的 beanClass 属性修改为了MapperFactoryBean
的 Class 对象
添加了一个入参为 Mapper 接口,这样初始化的 Spring Bean 就是该 Mapper 接口对应的 MapperFactoryBean
对象了
添加MapperFactoryBean
对象的sqlSessionTemplate
属性
org.mybatis.spring.annotation.@MapperScan
注解,指定须要扫描的包,将包中符合条件的 Mapper 接口,注册成 beanClass
为 MapperFactoryBean 的 BeanDefinition 对象,从而实现建立 Mapper 对象
咱们代码以下:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MapperScannerRegistrar.class) @Repeatable(MapperScans.class) public @interface MapperScan { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; Class<? extends Annotation> annotationClass() default Annotation.class; Class<?> markerInterface() default Class.class; String sqlSessionTemplateRef() default ""; String sqlSessionFactoryRef() default ""; Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class; String lazyInitialization() default ""; String defaultScope() default AbstractBeanDefinition.SCOPE_DEFAULT; }
value
和basePackage
都是指定 Mapper 接口的包路径@Import(MapperScannerRegistrar.class)
,该注解负责资源的导入,若是导入的是一个 Java 类,例如此处为 MapperScannerRegistrar 类,Spring 会将其注册成一个 Bean 对象org.mybatis.spring.annotation.MapperScannerRegistrar
:实现 ImportBeanDefinitionRegistrar、ResourceLoaderAware 接口
做为@MapperScann
注解的注册器,根据注解信息注册一个 MapperScannerConfigurer
对象,用于扫描 Mapper 接口
registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
方法
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 得到 @MapperScan 注解信息 AnnotationAttributes mapperScanAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null) { registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, // 生成 Bean 的名称,'org.springframework.core.type.AnnotationMetadata#MapperScannerRegistrar#0' generateBaseBeanName(importingClassMetadata, 0)); } }
generateBaseBeanName
方法,为MapperScannerConfigurer
生成一个beanName:org.springframework.core.type.AnnotationMetadata#MapperScannerRegistrar#0
registerBeanDefinitions
重载方法,注册一个类型为MapperScannerConfigurer
的 BeanDefinition 对象registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName)
方法
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) { // 建立一个 BeanDefinition 构建器,用于构建 MapperScannerConfigurer 的 BeanDefinition 对象 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); // 添加是否处理属性中的占位符属性 builder.addPropertyValue("processPropertyPlaceHolders", true); /* * 依次添加注解中的配置属性 */ Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass"); if (!Annotation.class.equals(annotationClass)) { builder.addPropertyValue("annotationClass", annotationClass); } Class<?> markerInterface = annoAttrs.getClass("markerInterface"); if (!Class.class.equals(markerInterface)) { builder.addPropertyValue("markerInterface", markerInterface); } Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator"); if (!BeanNameGenerator.class.equals(generatorClass)) { builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass)); } Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass); } String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef"); if (StringUtils.hasText(sqlSessionTemplateRef)) { builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef")); } String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef"); if (StringUtils.hasText(sqlSessionFactoryRef)) { builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef")); } /* * 获取到配置的 Mapper 接口的包路径 */ List<String> basePackages = new ArrayList<>(); basePackages.addAll( Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList())); basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText) .collect(Collectors.toList())); basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName) .collect(Collectors.toList())); /* * 若是没有 Mapper 接口的包路径,则默认使用注解类所在的包路径 */ if (basePackages.isEmpty()) { basePackages.add(getDefaultBasePackage(annoMeta)); } String lazyInitialization = annoAttrs.getString("lazyInitialization"); if (StringUtils.hasText(lazyInitialization)) { builder.addPropertyValue("lazyInitialization", lazyInitialization); } String defaultScope = annoAttrs.getString("defaultScope"); if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) { builder.addPropertyValue("defaultScope", defaultScope); } // 添加 Mapper 接口的包路径属性 builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages)); // 往 BeanDefinitionRegistry 注册表注册 MapperScannerConfigurer 对应的 BeanDefinition 对象 registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); }
MapperScannerConfigurer
的 BeanDefinition 对象processPropertyPlaceHolders
@MapperScan
注解中的配置属性,例如:sqlSessionFactoryBeanName
和basePackages
MapperScannerConfigurer
类型的 BeanDefinition 对象这样在 Spring 容器初始化的过程当中,会建立一个 MapperScannerConfigurer 对象,而后回到MapperScannerConfigurer的postProcessBeanDefinitionRegistry方法中,对包路径下的 Mapper 接口进行解析,前面已经分析过了
MapperScannerRegistrar的内部类,代码以下:
static class RepeatingRegistrar extends MapperScannerRegistrar { /** * {@inheritDoc} */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes mapperScansAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName())); if (mapperScansAttrs != null) { // 获取 MapperScan 注解数组 AnnotationAttributes[] annotations = mapperScansAttrs.getAnnotationArray("value"); /* * 依次处理每一个 MapperScan 注解 */ for (int i = 0; i < annotations.length; i++) { registerBeanDefinitions(importingClassMetadata, annotations[i], registry, generateBaseBeanName(importingClassMetadata, i)); } } } }
@MapperScan
注解上面的@Repeatable(MapperScans.class)
信息,能够看到若是同一个类上面定义多个@MapperScan
注解,则会生成对应的@MapperScans
注解@MapperScans
注解,依次处理@MapperScan
注解的信息除了配置 MapperScannerConfigurer 对象和经过 @MapperScan 注解扫描 Mapper 接口之外,咱们还能够经过 MyBatis 提供的 scan
标签来扫描 Mapper 接口
<mybatis:scan base-package="org.mybatis.spring.sample.mapper" />
在 META-INF/spring.schemas
定义以下:
http\://mybatis.org/schema/mybatis-spring-1.2.xsd=org/mybatis/spring/config/mybatis-spring.xsd http\://mybatis.org/schema/mybatis-spring.xsd=org/mybatis/spring/config/mybatis-spring.xsd
http://mybatis.org/schema/mybatis-spring-1.2.xsd
或 http://mybatis.org/schema/mybatis-spring.xsd
在 META-INF/spring.handlers
定义以下:
http\://mybatis.org/schema/mybatis-spring=org.mybatis.spring.config.NamespaceHandler
org.mybatis.spring.config.NamespaceHandler
:继承 NamespaceHandlerSupport 抽象类,MyBatis 的 XML Namespace 的处理器,代码以下:
public class NamespaceHandler extends NamespaceHandlerSupport { /** * {@inheritDoc} */ @Override public void init() { registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser()); } }
<mybatis:scan />
标签,使用 MapperScannerBeanDefinitionParser 解析org.mybatis.spring.config.MapperScannerBeanDefinitionParser
:实现 BeanDefinitionParser 接口,<mybatis:scan />
的解析器,代码以下:
public class MapperScannerBeanDefinitionParser extends AbstractBeanDefinitionParser { private static final String ATTRIBUTE_BASE_PACKAGE = "base-package"; private static final String ATTRIBUTE_ANNOTATION = "annotation"; private static final String ATTRIBUTE_MARKER_INTERFACE = "marker-interface"; private static final String ATTRIBUTE_NAME_GENERATOR = "name-generator"; private static final String ATTRIBUTE_TEMPLATE_REF = "template-ref"; private static final String ATTRIBUTE_FACTORY_REF = "factory-ref"; private static final String ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS = "mapper-factory-bean-class"; private static final String ATTRIBUTE_LAZY_INITIALIZATION = "lazy-initialization"; private static final String ATTRIBUTE_DEFAULT_SCOPE = "default-scope"; /** * 这个方法和 {@link MapperScannerRegistrar} 同样的做用 * 解析 <mybatis:scan /> 标签中的配置信息,设置到 MapperScannerConfigurer 的 BeanDefinition 对象中 * {@inheritDoc} * * @since 2.0.2 */ @Override protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { // 建立一个 BeanDefinition 构建器,用于构建 MapperScannerConfigurer 的 BeanDefinition 对象 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); // 添加是否处理属性中的占位符属性 builder.addPropertyValue("processPropertyPlaceHolders", true); /* * 解析 `scan` 标签中的配置,添加到 BeanDefinition 中 */ try { String annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION); if (StringUtils.hasText(annotationClassName)) { @SuppressWarnings("unchecked") Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) classLoader .loadClass(annotationClassName); builder.addPropertyValue("annotationClass", annotationClass); } String markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE); if (StringUtils.hasText(markerInterfaceClassName)) { Class<?> markerInterface = classLoader.loadClass(markerInterfaceClassName); builder.addPropertyValue("markerInterface", markerInterface); } String nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR); if (StringUtils.hasText(nameGeneratorClassName)) { Class<?> nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName); BeanNameGenerator nameGenerator = BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class); builder.addPropertyValue("nameGenerator", nameGenerator); } String mapperFactoryBeanClassName = element.getAttribute(ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS); if (StringUtils.hasText(mapperFactoryBeanClassName)) { @SuppressWarnings("unchecked") Class<? extends MapperFactoryBean> mapperFactoryBeanClass = (Class<? extends MapperFactoryBean>) classLoader .loadClass(mapperFactoryBeanClassName); builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass); } } catch (Exception ex) { XmlReaderContext readerContext = parserContext.getReaderContext(); readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause()); } builder.addPropertyValue("sqlSessionTemplateBeanName", element.getAttribute(ATTRIBUTE_TEMPLATE_REF)); builder.addPropertyValue("sqlSessionFactoryBeanName", element.getAttribute(ATTRIBUTE_FACTORY_REF)); builder.addPropertyValue("lazyInitialization", element.getAttribute(ATTRIBUTE_LAZY_INITIALIZATION)); builder.addPropertyValue("defaultScope", element.getAttribute(ATTRIBUTE_DEFAULT_SCOPE)); builder.addPropertyValue("basePackage", element.getAttribute(ATTRIBUTE_BASE_PACKAGE)); return builder.getBeanDefinition(); } /** * {@inheritDoc} * * @since 2.0.2 */ @Override protected boolean shouldGenerateIdAsFallback() { return true; } }
<mybatis:scan />
标签中的配置信息org.mybatis.spring.SqlSessionTemplate
:实现 SqlSession 和 DisposableBean 接口,SqlSession 操做模板实现类
实际上,代码实现和 org.apache.ibatis.session.SqlSessionManager
类似,承担 SqlSessionFactory 和 SqlSession 的职责
public class SqlSessionTemplate implements SqlSession, DisposableBean { /** * a factory of SqlSession */ private final SqlSessionFactory sqlSessionFactory; /** * {@link Configuration} 中默认的 Executor 执行器类型,默认 SIMPLE */ private final ExecutorType executorType; /** * SqlSessionInterceptor 代理对象 */ private final SqlSession sqlSessionProxy; /** * 异常转换器,MyBatisExceptionTranslator 对象 */ 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) { notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; // 建立一个 SqlSession 的动态代理对象,代理类为 SqlSessionInterceptor this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); } }
sqlSessionFactory
:用于建立 SqlSession 对象executorType
:执行器类型,建立 SqlSession 对象时根据它建立对应的 Executor 执行器,默认为sqlSessionProxy
:SqlSession 的动态代理对象,代理类为 SqlSessionInterceptor
exceptionTranslator
:异常转换器在调用SqlSessionTemplate中的SqlSession相关方法时,内部都是直接调用sqlSessionProxy
动态代理对象的方法,咱们来看看是如何处理的
SqlSessionTemplate的内部类,实现了 InvocationHandler 接口,做为sqlSessionProxy
动态代理对象的代理类,对 SqlSession 的相关方法进行加强
代码以下:
private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // <1> 获取一个 SqlSession 对象 SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { // 执行 SqlSession 的方法 Object result = method.invoke(sqlSession, args); // 当前 SqlSession 不处于 Spring 托管的事务中 if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() // 强制提交 sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 // 关闭 SqlSession 会话,释放资源 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; // 对异常进行转换,差很少就是转换成 MyBatis 的异常 Throwable translated = SqlSessionTemplate.this.exceptionTranslator .translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } }
SqlSessionUtils
的getSqlSession
方法,获取一个 SqlSession 对象SqlSessionUtils
的closeSqlSession
方法,“关闭”SqlSession 对象,这里的关闭不是真正的关闭org.mybatis.spring.SqlSessionHolder
:继承 org.springframework.transaction.support.ResourceHolderSupport
抽象类,SqlSession 持有器,用于保存当前 SqlSession 对象,保存到 org.springframework.transaction.support.TransactionSynchronizationManager
中,代码以下:
public final class SqlSessionHolder extends ResourceHolderSupport { /** * SqlSession 对象 */ private final SqlSession sqlSession; /** * 执行器类型 */ private final ExecutorType executorType; /** * PersistenceExceptionTranslator 对象 */ private final PersistenceExceptionTranslator exceptionTranslator; public SqlSessionHolder(SqlSession sqlSession, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sqlSession, "SqlSession must not be null"); notNull(executorType, "ExecutorType must not be null"); this.sqlSession = sqlSession; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; } }
org.mybatis.spring.SqlSessionUtils
:SqlSession 工具类,负责处理 MyBatis SqlSession 的生命周期,借助 Spring 的 TransactionSynchronizationManager 事务管理器管理 SqlSession 对象
getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)
方法,注释以下:
Gets an SqlSession from Spring Transaction Manager or creates a new one if needed. Tries to get a SqlSession out of current transaction.
If there is not any, it creates a new one. Then, it synchronizes the SqlSession with the transaction if Spring TX is active and SpringManagedTransactionFactory is configured as a transaction manager.
从事务管理器(线程安全)中获取一个 SqlSession 对象,若是不存在则建立一个 SqlSession,而后注册到事务管理器中,方法以下:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED); // 从 Spring 事务管理器中获取一个 SqlSessionHolder 对象 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); // 获取到 SqlSession 对象 SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } LOGGER.debug(() -> "Creating a new SqlSession"); // 上面没有获取到,则建立一个 SqlSession session = sessionFactory.openSession(executorType); // 将上面建立的 SqlSession 封装成 SqlSessionHolder,往 Spring 事务管理器注册 registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }
从 Spring 事务管理器中,根据 SqlSessionFactory 获取一个 SqlSessionHolder 对象
调用 sessionHolder
方法,获取到 SqlSession 对象,方法以下
private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) { SqlSession session = null; if (holder != null && holder.isSynchronizedWithTransaction()) { // 若是执行器类型发生了变动,抛出 TransientDataAccessResourceException 异常 if (holder.getExecutorType() != executorType) { throw new TransientDataAccessResourceException( "Cannot change the ExecutorType when there is an existing transaction"); } // 增长计数,关闭 SqlSession 时使用 holder.requested(); LOGGER.debug(() -> "Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction"); // 得到 SqlSession 对象 session = holder.getSqlSession(); } return session; }
若是 SqlSession 对象不为 null,则直接返回,接下来会建立一个
上面没有获取到,则建立一个 SqlSession 对象
调用 registerSessionHolder
方法,将上面建立的 SqlSession 封装成 SqlSessionHolder,往 Spring 事务管理器注册
返回新建立的 SqlSession 对象
registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session)
方法
注释以下:
Register session holder if synchronization is active (i.e. a Spring TX is active).
Note: The DataSource used by the Environment should be synchronized with the transaction either through DataSourceTxMgr or another tx synchronization.
Further assume that if an exception is thrown, whatever started the transaction will handle closing / rolling back the Connection associated with the SqlSession.
若是事务管理器处于激活状态,则将 SqlSession 封装成 SqlSessionHolder 对象注册到其中,方法以下:
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { SqlSessionHolder holder; if (TransactionSynchronizationManager.isSynchronizationActive()) { Environment environment = sessionFactory.getConfiguration().getEnvironment(); // <1> 若是使用 Spring 事务管理器 if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]"); // <1.1> 建立 SqlSessionHolder 对象 holder = new SqlSessionHolder(session, executorType, exceptionTranslator); // <1.2> 绑定到 TransactionSynchronizationManager 中 TransactionSynchronizationManager.bindResource(sessionFactory, holder); // <1.3> 建立 SqlSessionSynchronization 到 TransactionSynchronizationManager 中 TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); // <1.4> 设置同步 holder.setSynchronizedWithTransaction(true); // <1.5> 增长计数 holder.requested(); } else { // <2> 若是非 Spring 事务管理器,抛出 TransientDataAccessResourceException 异常 if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) { LOGGER.debug(() -> "SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional"); } else { throw new TransientDataAccessResourceException( "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization"); } } } else { LOGGER.debug(() -> "SqlSession [" + session + "] was not registered for synchronization because synchronization is not active"); } }
holder
SqlSessionSynchronization
对象(事务同步器)到 TransactionSynchronizationManager 中holder
的 synchronizedWithTransaction
属性为ture,和事务绑定了holder
的 referenceCount
引用数量closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory)
方法,注释以下:
Checks if SqlSession passed as an argument is managed by Spring TransactionSynchronizationManager
If it is not, it closes it, otherwise it just updates the reference counter and lets Spring call the close callback when the managed transaction ends
若是 SqlSessionFactory 是由 Spring 的事务管理器管理,而且和入参中的 session
相同,那么只进行释放,也就是将 referenceCount
引用数量减一,不然就直接关闭了
方法以下:
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) { notNull(session, NO_SQL_SESSION_SPECIFIED); notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); // <1> 从 TransactionSynchronizationManager 中,得到 SqlSessionHolder 对象 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); // <2.1> 若是相等,说明在 Spring 托管的事务中,则释放 holder 计数 if ((holder != null) && (holder.getSqlSession() == session)) { LOGGER.debug(() -> "Releasing transactional SqlSession [" + session + "]"); holder.released(); } else { // <2.2> 若是不相等,说明不在 Spring 托管的事务中,直接关闭 SqlSession 对象 LOGGER.debug(() -> "Closing non transactional SqlSession [" + session + "]"); session.close(); } }
isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory)
方法,判断 SqlSession 对象是否被 Sping 的事务管理器管理,代码以下:
public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) { notNull(session, NO_SQL_SESSION_SPECIFIED); notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); // 从 TransactionSynchronizationManager 中,得到 SqlSessionHolder 对象 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); // 若是相等,说明在 Spring 托管的事务中 return (holder != null) && (holder.getSqlSession() == session); }
org.mybatis.spring.SqlSessionUtils
的内部类,继承了 TransactionSynchronizationAdapter 抽象类,SqlSession 的事务同步器,基于 Spring Transaction 体系
注释以下:
Callback for cleaning up resources.
It cleans TransactionSynchronizationManager and also commits and closes the SqlSession.
It assumes that Connection life cycle will be managed by DataSourceTransactionManager or JtaTransactionManager
回调的时候清理资源
private static final class SqlSessionSynchronization extends TransactionSynchronizationAdapter { private final SqlSessionHolder holder; private final SqlSessionFactory sessionFactory; /** * 是否开启 */ private boolean holderActive = true; public SqlSessionSynchronization(SqlSessionHolder holder, SqlSessionFactory sessionFactory) { notNull(holder, "Parameter 'holder' must be not null"); notNull(sessionFactory, "Parameter 'sessionFactory' must be not null"); this.holder = holder; this.sessionFactory = sessionFactory; } }
@Override public int getOrder() { // order right before any Connection synchronization return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1; }
当事务挂起时,取消当前线程的绑定的 SqlSessionHolder 对象,方法以下:
@Override public void suspend() { if (this.holderActive) { LOGGER.debug(() -> "Transaction synchronization suspending SqlSession [" + this.holder.getSqlSession() + "]"); TransactionSynchronizationManager.unbindResource(this.sessionFactory); } }
当事务恢复时,从新绑定当前线程的 SqlSessionHolder 对象,方法以下:
@Override public void resume() { if (this.holderActive) { LOGGER.debug(() -> "Transaction synchronization resuming SqlSession [" + this.holder.getSqlSession() + "]"); TransactionSynchronizationManager.bindResource(this.sessionFactory, this.holder); } }
在事务提交以前,调用 SqlSession#commit() 方法以前,提交事务。虽说,Spring 自身也会调用 Connection#commit() 方法,进行事务的提交。可是,SqlSession#commit() 方法中,不只仅有事务的提交,还有提交批量操做,刷新本地缓存等等,方法以下:
@Override public void beforeCommit(boolean readOnly) { // Connection commit or rollback will be handled by ConnectionSynchronization or DataSourceTransactionManager. // But, do cleanup the SqlSession / Executor, including flushing BATCH statements so they are actually executed. // SpringManagedTransaction will no-op the commit over the jdbc connection // TODO This updates 2nd level caches but the tx may be rolledback later on! if (TransactionSynchronizationManager.isActualTransactionActive()) { try { LOGGER.debug(() -> "Transaction synchronization committing SqlSession [" + this.holder.getSqlSession() + "]"); // 提交事务 this.holder.getSqlSession().commit(); } catch (PersistenceException p) { // 若是发生异常,则进行转换,并抛出异常 if (this.holder.getPersistenceExceptionTranslator() != null) { DataAccessException translated = this.holder.getPersistenceExceptionTranslator() .translateExceptionIfPossible(p); if (translated != null) { throw translated; } } throw p; } } }
提交事务完成以前,关闭 SqlSession 对象,在 beforeCommit 以后调用,方法以下:
@Override public void beforeCompletion() { // Issue #18 Close SqlSession and deregister it now // because afterCompletion may be called from a different thread if (!this.holder.isOpen()) { LOGGER.debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]"); // 取消当前线程的绑定的 SqlSessionHolder 对象 TransactionSynchronizationManager.unbindResource(sessionFactory); // 标记无效 this.holderActive = false; LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]"); // 关闭 SqlSession 对象 this.holder.getSqlSession().close(); } }
在事务完成以后,关闭 SqlSession 对象,解决可能出现的跨线程的状况,方法以下:
@Override public void afterCompletion(int status) { if (this.holderActive) { // 处于有效状态 // afterCompletion may have been called from a different thread // so avoid failing if there is nothing in this one LOGGER.debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]"); // 取消当前线程的绑定的 SqlSessionHolder 对象 TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory); // 标记无效 this.holderActive = false; LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]"); // 关闭 SqlSession 对象 this.holder.getSqlSession().close(); } this.holder.reset(); }
还有一部份内容在org.mybatis.spring.batch
包路径下,基于 Spring Batch 框架,Spring 和 MyBatis 的批处理进行集成,感兴趣的小伙伴能够去阅读一下
我一般都是经过配置示例中方式配置MyBatis的,由于我以为配置文件易于维护,比较可观,固然也经过注解(@MapperScan
)的方式进行配置,原理相同
首先配置DataSource
数据源的 Sping Bean,咱们一般不会使用 MyBatis 自带的数据源,由于其性能很差,都是经过Druid
或者HikariCP
等第三方组件来实现
配置SqlSessionFactoryBean
的 Spring Bean,设置数据源属性dataSource
,还能够配置configLocation
(mybatis-config.xml配置文件的路径)、mapperLocations
(XML映射文件的路径)等属性,这样让 Spring 和 MyBatis 完美的整合到一块儿了
配置MapperScannerConfigurer
的 Spring Bean,设置basePackage
(须要扫描的Mapper接口的路径)、sqlSessionFactoryBeanName
(上面定义的SqlSessionFactoryBean)等属性
由于实现了 BeanDefinitionRegistryPostProcessor
接口,在这些 Mapper 接口的 BeanDefinition 对象(Spring Bean 的前身)注册完毕后,能够进行一些处理
在这里会修改这些 BeanDefinition 对象为 MapperFactoryBean
类型,在初始化 Spring Bean 的过程当中则建立的是 MapperFactoryBean 对象,注入该对象则会调用其 getObject()
方法,返回的该 Mapper 接口对应的动态代理对象
这样当你注入 Mapper 接口时,实际注入的是其动态代理对象
在SqlSessionTemplate
对象中,承担 SqlSessionFactory 和 SqlSession 的职责
到这里,相信你们对 MyBatis 集成到 Spring 的方案有了必定的了解,感谢你们的阅读!!!😄😄😄
参考文章:芋道源码《精尽 MyBatis 源码分析》