MyBatis源码分析(七)MyBatis与Spring的整合原理与源码分析
标签: MyBatis源码分析(七)MyBatis与Spring的整合原理与源码分析 博客 51CTO博客
2023-04-03 18:23:45 228浏览
文章目录
- 写在前面
- 一、SqlSessionFactoryBean配置SqlSessionFactory
- 1、初识SqlSessionFactoryBean
- 2、实现ApplicationListener
- 3、实现InitializingBean接口
- 4、实现FactoryBean接口
- 5、构建SqlSessionFactory
- 二、SqlSessionTemplate
- 1、初始SqlSessionTemplate
- 2、SqlSessionTemplate的构造方法
- 3、SqlSessionInterceptor执行核心方法
- (1)获取SqlSession
- (2)关闭SqlSession
- 4、总结
- 三、@MapperScan扫描注册的Mapper
- 1、@MapperScan
- 2、MapperScannerRegistrar
- 3、MapperScannerConfigurer
- (1)processBeanDefinitions
- 4、MapperFactoryBean
- 四、总结
- 写在后面
写在前面
MyBatis与Spring的整合,依赖于mybatis-spring包。
其中的设计非常的巧妙,并且与Spring完美结合,我们一起来完整分析一下MyBatis是怎么与Spring整合的。
一、SqlSessionFactoryBean配置SqlSessionFactory
1、初识SqlSessionFactoryBean
SqlSessionFactoryBean是MyBatis与Spring整合的关键,它用于创建MyBatis的SqlSessionFactory。
SqlSessionFactoryBean实现了FactoryBean、InitializingBean和ApplicationListener,这都是Spring的核心接口。
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
并且,SqlSessionFactoryBean中包含了所有的MyBatis的配置项,包括但不限于数据源、事务管理工厂、环境、拦截器、TypeHandler、别名、缓存等等。并且提供了这些属性的get和set方法,开发人员可以随心所欲地对这些属性进行配置。
private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();
private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();
private Resource configLocation;
private Configuration configuration;
private Resource[] mapperLocations;
private DataSource dataSource;
private TransactionFactory transactionFactory;
private Properties configurationProperties;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
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;
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;
2、实现ApplicationListener
Spring的事件监听器,关于Spring的事件监听器请移步:
Spring事件详解,Spring-Event源码详解,一文搞透Spring事件管理
重写了onApplicationEvent方法:
// org.mybatis.spring.SqlSessionFactoryBean#onApplicationEvent
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (failFast && event instanceof ContextRefreshedEvent) {
// fail-fast -> check all statements are completed
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
}
}
主要是做了快速失败语句验证。
3、实现InitializingBean接口
在Spring的Bean生命周期中,一个Bean实现了InitializingBean接口,会在Bean的初始化过程中调用其afterPropertiesSet方法:
// org.mybatis.spring.SqlSessionFactoryBean#afterPropertiesSet
@Override
public void afterPropertiesSet() throws Exception {
// 校验
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
// 关键方法!构造SqlSessionFactory
this.sqlSessionFactory = buildSqlSessionFactory();
}
4、实现FactoryBean接口
FactoryBean接口是Spring-bean的创建工厂,SpringIOC的第三级缓存中存储的就是FactoryBean,其getObject方法默认就会公开单例实例或创建一个新的原型实例,以下是Spring中FactoryBean的默认实现:
// org.springframework.beans.factory.config.AbstractFactoryBean#getObject
@Override
public final T getObject() throws Exception {
if (isSingleton()) {
return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
}
else {
return createInstance();
}
}
而此处的SqlSessionFactoryBean重写了getObject方法,直接返回一个SqlSessionFactory(若没有,走创建逻辑)。相当于getObject逻辑不需要Spring来掌控,交给自己来做,生成一个SqlSessionFactory的实例。
// org.mybatis.spring.SqlSessionFactoryBean#getObject
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
5、构建SqlSessionFactory
在buildSqlSessionFactory方法中,包含着对SqlSessionFactory的创建逻辑:
//
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
final Configuration targetConfiguration;
// 创建一个MyBatis的Configuration
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
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) {
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");
targetConfiguration = new Configuration();
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
}
Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
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);
}
if (!isEmpty(this.typeAliases)) {
Stream.of(this.typeAliases).forEach(typeAlias -> {
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
});
}
if (!isEmpty(this.plugins)) {
Stream.of(this.plugins).forEach(plugin -> {
targetConfiguration.addInterceptor(plugin);
LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
});
}
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);
}
if (!isEmpty(this.typeHandlers)) {
Stream.of(this.typeHandlers).forEach(typeHandler -> {
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
});
}
if (!isEmpty(this.scriptingLanguageDrivers)) {
Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
targetConfiguration.getLanguageRegistry().register(languageDriver);
LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
});
}
Optional.ofNullable(this.defaultScriptingLanguageDriver)
.ifPresent(targetConfiguration::setDefaultScriptingLanguage);
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);
}
}
Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
if (xmlConfigBuilder != null) {
try {
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();
}
}
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
if (this.mapperLocations != null) {
if (this.mapperLocations.length == 0) {
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
} else {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
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.");
}
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
我们可以看出,会将当前SqlSessionFactoryBean中的所有用户加载的配置,都在这里加载到MyBatis的Configuration中,并且调用sqlSessionFactoryBuilder的build方法进行构建SqlSessionFactory,而这个操作也正是MyBatis的标准构建SqlSessionFactoryAPI。
二、SqlSessionTemplate
1、初始SqlSessionTemplate
MyBatis为了与Spring整合,提供了一个SqlSessionTemplate来管理SqlSession,通过代理SqlSession的方式将其与SqlSession进行关联:
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
SqlSessionTemplate 实现了SqlSession,并重写了SqlSession的接口,而SqlSession的这些接口,全都是通过sqlSessionProxy属性进行代理:
@Override
public <E> List<E> selectList(String statement) {
return this.sqlSessionProxy.selectList(statement);
}
/**
* {@inheritDoc}
*/
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.selectList(statement, parameter);
}
/**
* {@inheritDoc}
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.selectList(statement, parameter, rowBounds);
}
// 等等都是这样处理的
2、SqlSessionTemplate的构造方法
在创建SqlSessionTemplate时,会执行该构造方法:
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;
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
最终通过JDK动态代理的方式,创建SqlSession的代理,并且创建一个SqlSessionInterceptor处理器。
3、SqlSessionInterceptor执行核心方法
SqlSessionInterceptor 实现了InvocationHandler,也就是说调用SqlSession的方法时,会执行SqlSessionInterceptor 的invoke方法:
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取SqlSession
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
// 执行核心方法(select、update、delete等)
Object result = method.invoke(sqlSession, args);
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
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
(1)获取SqlSession
获取SqlSession的主逻辑和MyBatis的API没什么不同,只不过此处会通过TransactionSynchronizationManager来缓存SqlSession。
// org.mybatis.spring.SqlSessionUtils#getSqlSession(org.apache.ibatis.session.SqlSessionFactory, org.apache.ibatis.session.ExecutorType, org.springframework.dao.support.PersistenceExceptionTranslator)
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
// ThreadLocal的线程持有者,从事务管理器中获取
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
// 如果没有,创建一个新的SqlSession
LOGGER.debug(() -> "Creating a new SqlSession");
session = sessionFactory.openSession(executorType);
// 存储SqlSession到ThreadLocal
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
TransactionSynchronizationManager的getResource方法,最终的resource是一个ThreadLocal,我们可以得出结论,同一个线程获取的SqlSession是同一个,不同线程获取的SqlSession是不同的:
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
// org.springframework.transaction.support.TransactionSynchronizationManager#doGetResource
@Nullable
private static Object doGetResource(Object actualKey) {
Map<Object, Object> map = resources.get();
if (map == null) {
return null;
}
Object value = map.get(actualKey);
// Transparently remove ResourceHolder that was marked as void...
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
map.remove(actualKey);
// Remove entire ThreadLocal if empty...
if (map.isEmpty()) {
resources.remove();
}
value = null;
}
return value;
}
在registerSessionHolder逻辑中,只有开启了事务、并且事务工厂是SpringManagedTransactionFactory,才会缓存SqlSession:
// org.mybatis.spring.SqlSessionUtils#registerSessionHolder
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
SqlSessionHolder holder;
// 如果当前线程的事务同步是活动的,则返回。
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
TransactionSynchronizationManager
.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
holder.setSynchronizedWithTransaction(true);
holder.requested();
} else {
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");
}
}
(2)关闭SqlSession
关闭SqlSession同样会先从ThreadLocal中释放,然后调用SqlSession的close方法
// org.mybatis.spring.SqlSessionUtils#closeSqlSession
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, NO_SQL_SESSION_SPECIFIED);
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
if ((holder != null) && (holder.getSqlSession() == session)) {
LOGGER.debug(() -> "Releasing transactional SqlSession [" + session + "]");
holder.released();
} else {
LOGGER.debug(() -> "Closing non transactional SqlSession [" + session + "]");
session.close();
}
}
4、总结
也就是说,通过代理SqlSession,每次执行SqlSession的select、update等方法时,会先执行获取SqlSession的方法,然后再调用select方法。
也就是说,我们做的sqlSessionFactory.openSession这个操作,交给框架的代理做了。
三、@MapperScan扫描注册的Mapper
1、@MapperScan
@MapperScan用于扫描Mapper所在的包,注册了一个MapperScannerRegistrar
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
2、MapperScannerRegistrar
MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar,在registerBeanDefinitions方法中,进行了MapperScannerConfigurer的注册:
// org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
// 定义一个MapperScannerConfigurer的bean,并且设置许多参数
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"));
}
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()));
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
3、MapperScannerConfigurer
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,在其postProcessBeanDefinitionRegistry方法中,创建了ClassPathMapperScanner 进行了扫描和注册:
// org.mybatis.spring.mapper.MapperScannerConfigurer#postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
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));
}
scanner.registerFilters();
scanner.scan( // 扫描注册过程
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
// org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
// 扫描注册
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
// org.mybatis.spring.mapper.ClassPathMapperScanner#doScan
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// Spring的扫描+Bean的注册
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 {
// Mapper的最终属性调整过程
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
(1)processBeanDefinitions
在processBeanDefinitions方法中,对BeanDefinition进行属性调整,关键逻辑-设置Bean的Class为MapperFactoryBean,因为Mapper都是接口无法实例化,所以只能通过MapperFactoryBean来进行实例化。
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
// org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
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
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBeanClass); // 设置Bean的Class
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
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;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
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;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}
4、MapperFactoryBean
MapperFactoryBean实现了FactoryBean,同样重写了getObject方法:
// org.mybatis.spring.mapper.MapperFactoryBean#getObject
@Override
public T getObject() throws Exception {
//
return getSqlSession().getMapper(this.mapperInterface);
}
看到这,我们是不是眼前一亮!
正是这里,获取了SqlSession,并且调用了getMapper方法获取了这个接口!
这个Mapper接口注册到Spring容器中,并通过这种方式获取。
四、总结
到此,MyBatis与Spring的整合全流程,我们已经彻底分析完毕了。
是不是让人感叹,学好源码真不容易,看大佬写的框架真实太精彩了!
写在后面
如果本文对你有帮助,请点赞收藏关注一下吧 ~
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
展开评论