上一篇文章我们讲了SqlSessionFactoryBean,通过这个FactoryBean创建SqlSessionFactory并注册进Spring容器,这篇文章我们就讲剩下的部分,通过MapperScannerConfigurer将Mapper接口生成代理注入到Spring

扫描Mapper接口

我们上一篇文章介绍了扫描Mapper接口有两种方式,一种是通过bean.xml注册MapperScannerConfigurer对象,一种是通过@MapperScan("com.chenhao.mapper")注解的方式,如下

方式一:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.chenhao.mapper" />
</bean>

方式二:

@Configuration
@MapperScan("com.chenhao.mapper")
public class AppConfig {

@MapperScan

我们来看看@MapperScan这个注解

@Import(MapperScannerRegistrar.class)
public @interface MapperScan {

MapperScan使用@Import将MapperScannerRegistrar导入。

MapperScannerRegistrar

 public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取MapperScan 注解,如@MapperScan("com.chenhao.mapper")
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
// 创建路径扫描器,下面的一大段都是将MapperScan 中的属性设置到ClassPathMapperScanner ,做的就是一个set操作
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// this check is needed in Spring 3.1
if (resourceLoader != null) {
// 设置资源加载器,作用:扫描指定包下的class文件。
scanner.setResourceLoader(resourceLoader);
}
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
//设置SqlSessionFactory的名称
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<String>();
//获取配置的包路径,如com.chenhao.mapper
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
// 注册过滤器,作用:什么类型的Mapper将会留下来。
scanner.registerFilters();
// 扫描指定包
scanner.doScan(StringUtils.toStringArray(basePackages));
}
}

ClassPathMapperScanner

接着我们来看看扫描过程 scanner.doScan(StringUtils.toStringArray(basePackages));

 @Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//调用父类进行扫描,并将basePackages下的class都封装成BeanDefinitionHolder,并注册进Spring容器的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 {
//继续对beanDefinitions做处理,额外设置一些属性
processBeanDefinitions(beanDefinitions);
} return beanDefinitions;
} protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
//遍历basePackages进行扫描
for (String basePackage : basePackages) {
//找出匹配的类
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
//封装成BeanDefinitionHolder 对象
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//将BeanDefinition对象注入spring的BeanDefinitionMap中,后续getBean时,就是从BeanDefinitionMap获取对应的BeanDefinition对象,取出其属性进行实例化Bean
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}

我们重点看下第4行和第10行代码,第4行是调用父类的doScan方法,获取basePackages下的所有Class,并将其生成BeanDefinition,注入spring的BeanDefinitionMap中,也就是Class的描述类,在调用getBean方法时,获取BeanDefinition进行实例化。此时,所有的Mapper接口已经被生成了BeanDefinition。接着我们看下第10行,对生成的BeanDefinition做一些额外的处理。

processBeanDefinitions

上面BeanDefinition已经注入进Spring容器了,接着我们看对BeanDefinition进行哪些额外的处理

 private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
} // 设置definition构造器的输入参数为definition.getBeanClassName(),这里就是com.chenhao.mapper.UserMapper
// 那么在getBean实例化时,通过反射调用构造器实例化时要将这个参数传进去
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName())
// 修改definition对应的Class
// 看过Spring源码的都知道,getBean()返回的就是BeanDefinitionHolder中beanClass属性对应的实例
// 所以我们后面ac.getBean(UserMapper.class)的返回值也就是MapperFactoryBean的实例
// 但是最终被注入到Spring容器的对象的并不是MapperFactoryBean的实例,根据名字看,我们就知道MapperFactoryBean实现了FactoryBean接口
// 那么最终注入Spring容器的必定是从MapperFactoryBean的实例的getObject()方法中返回
definition.setBeanClass(this.mapperFactoryBean.getClass()); 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设置属性值sqlSessionFactory,那么在MapperFactoryBean实例化后,进行属性赋值时populateBean(beanName, mbd, instanceWrapper);,会通过反射调用sqlSessionFactory的set方法进行赋值
//也就是在MapperFactoryBean创建实例后,要调用setSqlSessionFactory方法将sqlSessionFactory注入进MapperFactoryBean实例
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) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}

看第19行,将definition的beanClass属性设置为MapperFactoryBean.class,我们知道,在getBean的时候,会通过反射创建Bean的实例,也就是创建beanClass的实例,如下Spring的getBean的部分代码:

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// 没有覆盖
// 直接使用反射实例化即可
if (!bd.hasMethodOverrides()) {
// 重新检测获取下构造函数
// 该构造函数是经过前面 N 多复杂过程确认的构造函数
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
// 获取已经解析的构造函数
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
// 如果为 null,从 class 中解析获取,并设置
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(
(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
}
else {
//利用反射获取构造器
constructorToUse =
clazz.getDeclaredConstructor();
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
} // 通过BeanUtils直接使用构造器对象实例化bean
return
BeanUtils.instantiateClass(constructorToUse);
}
else {
// 生成CGLIB创建的子类对象
return
instantiateWithMethodInjection(bd, beanName, owner);
}
}

看到没,是通过bd.getBeanClass();从BeanDefinition中获取beanClass属性,然后通过反射实例化Bean,如上,所有的Mapper接口扫描封装成的BeanDefinition的beanClass都设置成了MapperFactoryBean,我们知道在Spring初始化的最后,会获取所有的BeanDefinition,并通过getBean创建所有的实例注入进Spring容器,那么意思就是说,在getBean时,创建的的所有Mapper对象是MapperFactoryBean这个对象了。

我们看下第13行处,设置了BeanDefinition构造器参数,那么当getBean中通过构造器创建实例时,需要将设置的构造器参数definition.getBeanClassName(),这里就是com.chenhao.mapper.UserMapper传进去。

还有一个点要注意,在第30行处,往BeanDefinition的PropertyValues设置了sqlSessionFactory,那么在创建完MapperFactoryBean的实例后,会对MapperFactoryBean进行属性赋值,也就是Spring创建Bean的这句代码,populateBean(beanName, mbd, instanceWrapper);,这里会通过反射调用MapperFactoryBean的setSqlSessionFactory方法将sqlSessionFactory注入进MapperFactoryBean实例,所以我们接下来重点看的就是MapperFactoryBean这个对象了。

MapperFactoryBean

接下来我们看最重要的一个类MapperFactoryBean

//继承SqlSessionDaoSupport、实现FactoryBean,那么最终注入Spring容器的对象要从getObject()中取
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true; public MapperFactoryBean() {
} //构造器,我们上一节中在BeanDefinition中已经设置了构造器输入参数
//所以在通过反射调用构造器实例化时,会获取在BeanDefinition设置的构造器输入参数
//也就是对应得每个Mapper接口Class
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
} protected void checkDaoConfig() {
super.checkDaoConfig();
Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = this.getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception var6) {
this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
throw new IllegalArgumentException(var6);
} finally {
ErrorContext.instance().reset();
}
} }
//最终注入Spring容器的就是这里的返回对象
public T getObject() throws Exception {
//获取父类setSqlSessionFactory方法中创建的SqlSessionTemplate
//通过SqlSessionTemplate获取mapperInterface的代理类
//我们例子中就是通过SqlSessionTemplate获取com.chenhao.mapper.UserMapper的代理类
//获取到Mapper接口的代理类后,就把这个Mapper的代理类对象注入Spring容器
return this.getSqlSession().getMapper(this.mapperInterface);
} public Class<T> getObjectType() {
return this.mapperInterface;
} public boolean isSingleton() {
return true;
} public void setMapperInterface(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
} public Class<T> getMapperInterface() {
return this.mapperInterface;
} public void setAddToConfig(boolean addToConfig) {
this.addToConfig = addToConfig;
} public boolean isAddToConfig() {
return this.addToConfig;
}
} public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSession sqlSession;
private boolean externalSqlSession; public SqlSessionDaoSupport() {
}
//还记得上一节中我们往BeanDefinition中设置的sqlSessionFactory这个属性吗?
//在实例化MapperFactoryBean后,进行属性赋值时,就会通过反射调用setSqlSessionFactory
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
//创建一个SqlSessionTemplate并赋值给sqlSession
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
} public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSession = sqlSessionTemplate;
this.externalSqlSession = true;
} public SqlSession getSqlSession() {
return this.sqlSession;
} protected void checkDaoConfig() {
Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
}
}

我们看到MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean,那么getBean获取的对象是从其getObject()中获取,并且MapperFactoryBean是一个单例,那么其中的属性SqlSessionTemplate对象也是一个单例,全局唯一,供所有的Mapper代理类使用。

这里我大概讲一下getBean时,这个类的过程:

1、MapperFactoryBean通过反射调用构造器实例化出一个对象,并且通过上一节中definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName())设置的构造器参数,在构造器实例化时,传入Mapper接口的Class,并设置为MapperFactoryBean的mapperInterface属性。

2、进行属性赋值,通过上一节中definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);设置的属性值,在populateBean属性赋值过程中通过反射调用setSqlSessionFactory方法,并创建SqlSessionTemplate对象设置到sqlSession属性中。

3、由于MapperFactoryBean实现了FactoryBean,最终注册进Spring容器的对象是从getObject()方法中取,接着获取SqlSessionTemplate这个SqlSession调用getMapper(this.mapperInterface);生成Mapper接口的代理对象,将Mapper接口的代理对象注册进Spring容器

至此,所有com.chenhao.mapper中的Mapper接口都生成了代理类,并注入到Spring容器了。接着我们就可以在Service中直接从Spring的BeanFactory中获取了,如下

SqlSessionTemplate

还记得我们前面分析Mybatis源码时,获取的SqlSession实例是什么吗?我们简单回顾一下

/**
* ExecutorType 指定Executor的类型,分为三种:SIMPLE, REUSE, BATCH,默认使用的是SIMPLE
* TransactionIsolationLevel 指定事务隔离级别,使用null,则表示使用数据库默认的事务隔离界别
* autoCommit 是否自动提交,传过来的参数为false,表示不自动提交
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取配置中的环境信息,包括了数据源信息、事务等
final Environment environment = configuration.getEnvironment();
// 创建事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建事务,配置事务属性
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建Executor,即执行器
// 它是真正用来Java和数据库交互操作的类,后面会展开说。
final Executor executor = configuration.newExecutor(tx, execType);
// 创建DefaultSqlSession对象返回,其实现了SqlSession接口
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

大家应该还有印象,就是上面的DefaultSqlSession,那上一节的SqlSessionTemplate是什么鬼???我们来看看

// 实现SqlSession接口,单例、线程安全,使用spring的事务管理器的sqlSession,
// 具体的SqlSession的功能,则是通过内部包含的sqlSessionProxy来来实现,这也是静态代理的一种实现。
// 同时内部的sqlSessionProxy实现InvocationHandler接口,则是动态代理的一种实现,而线程安全也是在这里实现的。
// 注意mybatis默认的sqlSession不是线程安全的,需要每个线程有一个单例的对象实例。
// SqlSession的主要作用是提供SQL操作的API,执行指定的SQL语句,mapper需要依赖SqlSession来执行其方法对应的SQL。
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
// 一个代理类,由于SqlSessionTemplate为单例的,被所有mapper,所有线程共享,
// 所以sqlSessionProxy要保证这些mapper中方法调用的线程安全特性:
// sqlSessionProxy的实现方式主要为实现了InvocationHandler接口实现了动态代理,
// 由动态代理的知识可知,InvocationHandler的invoke方法会拦截所有mapper的所有方法调用,
// 故这里的实现方式是在invoke方法内部创建一个sqlSession局部变量,从而实现了每个mapper的每个方法调用都使用
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());
} public SqlSessionFactory getSqlSessionFactory() {
return this.sqlSessionFactory;
} public ExecutorType getExecutorType() {
return this.executorType;
} public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
return this.exceptionTranslator;
} public <T> T selectOne(String statement) {
//由真实的对象sqlSessionProxy执行查询
return this.sqlSessionProxy.selectOne(statement);
} public <T> T selectOne(String statement, Object parameter) {
//由真实的对象sqlSessionProxy执行查询
return this.sqlSessionProxy.selectOne(statement, parameter);
} public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
//由真实的对象sqlSessionProxy执行查询
return this.sqlSessionProxy.selectMap(statement, mapKey);
} public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
//由真实的对象sqlSessionProxy执行查询
return this.sqlSessionProxy.selectMap(statement, parameter, mapKey);
} public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
//由真实的对象sqlSessionProxy执行查询
return this.sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds);
} public <T> Cursor<T> selectCursor(String statement) {
return this.sqlSessionProxy.selectCursor(statement);
} public <T> Cursor<T> selectCursor(String statement, Object parameter) {
return this.sqlSessionProxy.selectCursor(statement, parameter);
} public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.selectCursor(statement, parameter, rowBounds);
} public <E> List<E> selectList(String statement) {
return this.sqlSessionProxy.selectList(statement);
} public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.selectList(statement, parameter);
} public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.selectList(statement, parameter, rowBounds);
} public void select(String statement, ResultHandler handler) {
this.sqlSessionProxy.select(statement, handler);
} public void select(String statement, Object parameter, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, handler);
} public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
} public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
} public int insert(String statement, Object parameter) {
return this.sqlSessionProxy.insert(statement, parameter);
} public int update(String statement) {
return this.sqlSessionProxy.update(statement);
} public int update(String statement, Object parameter) {
return this.sqlSessionProxy.update(statement, parameter);
} public int delete(String statement) {
return this.sqlSessionProxy.delete(statement);
} public int delete(String statement, Object parameter) {
return this.sqlSessionProxy.delete(statement, parameter);
} public <T> T getMapper(Class<T> type) {
return this.getConfiguration().getMapper(type, this);
} public void commit() {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
} public void commit(boolean force) {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
} public void rollback() {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
} public void rollback(boolean force) {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
} public void close() {
throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
} public void clearCache() {
this.sqlSessionProxy.clearCache();
} public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();
} public Connection getConnection() {
return this.sqlSessionProxy.getConnection();
} public List<BatchResult> flushStatements() {
return this.sqlSessionProxy.flushStatements();
} public void destroy() throws Exception {
} private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); Object unwrapped;
try {
Object result = method.invoke(sqlSession, args);
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
} unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
} throw (Throwable)unwrapped;
} finally {
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
} } return unwrapped;
}
}
}

我们看到SqlSessionTemplate实现了SqlSession接口,那么Mapper代理类中执行所有的数据库操作,都是通过SqlSessionTemplate来执行,如上我们看到所有的数据库操作都由对象sqlSessionProxy执行查询

静态代理的使用

SqlSessionTemplate在内部访问数据库时,其实是委派给sqlSessionProxy来执行数据库操作的,SqlSessionTemplate不是自身重新实现了一套mybatis数据库访问的逻辑。

SqlSessionTemplate通过静态代理机制来提供SqlSession接口的行为,即实现SqlSession接口来获取SqlSession的所有方法;SqlSessionTemplate的定义如下:标准的静态代理实现模式,即实现SqlSession接口并在内部包含一个SqlSession接口实现类引用sqlSessionProxy。那我们就要看看sqlSessionProxy这个SqlSession,我们先来看看SqlSessionTemplate的构造方法

 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());
}

动态代理的使用

不是吧,又使用了动态代理??真够曲折的,那我们接着看 new SqlSessionTemplate.SqlSessionInterceptor() 这个InvocationHandler

private class SqlSessionInterceptor implements InvocationHandler {
//很奇怪,这里并没有真实目标对象?
private SqlSessionInterceptor() {
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取一个sqlSession来执行proxy的method对应的SQL,
// 每次调用都获取创建一个sqlSession线程局部变量,故不同线程相互不影响,在这里实现了SqlSessionTemplate的线程安全性
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); Object unwrapped;
try {
//直接通过新创建的SqlSession反射调用method
//这也就解释了为什么不需要目标类属性了,这里每次都会创建一个
Object result = method.invoke(sqlSession, args);
// 如果当前操作没有在一个Spring事务中,则手动commit一下
// 如果当前业务没有使用@Transation,那么每次执行了Mapper接口的方法直接commit
// 还记得我们前面讲的Mybatis的一级缓存吗,这里一级缓存不能起作用了,因为每执行一个Mapper的方法,sqlSession都提交了
// sqlSession提交,会清空一级缓存
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
} unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
} throw (Throwable)unwrapped;
} finally {
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
} }
return unwrapped;
}
}

这里大概讲一下Mapper代理类调用方法执行逻辑:

1、SqlSessionTemplate生成Mapper代理类时,将本身传进去做为Mapper代理类的属性,调用Mapper代理类的方法时,最后会通过SqlSession类执行,也就是调用SqlSessionTemplate中的方法。

2、SqlSessionTemplate中操作数据库的方法中又交给了sqlSessionProxy这个代理类去执行,那么每次执行的方法都会回调其SqlSessionInterceptor这个InvocationHandler的invoke方法

3、在invoke方法中,为每个线程创建一个新的SqlSession,并通过反射调用SqlSession的method。这里sqlSession是一个线程局部变量,不同线程相互不影响,实现了SqlSessionTemplate的线程安全性

4、如果当前操作并没有在Spring事务中,那么每次执行一个方法,都会提交,相当于数据库的事务自动提交,Mysql的一级缓存也将不可用

接下来我们还要看一个地方,invoke中是如何创建SqlSession的?

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
Assert.notNull(executorType, "No ExecutorType specified");
//通过TransactionSynchronizationManager内部的ThreadLocal中获取
SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if(session != null) {
return session;
} else {
if(LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
//这里我们知道实际上是创建了一个DefaultSqlSession
session = sessionFactory.openSession(executorType);
//将创建的SqlSession对象放入TransactionSynchronizationManager内部的ThreadLocal中
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
}

通过sessionFactory.openSession(executorType)实际创建的SqlSession还是DefaultSqlSession。如果读过我前面Spring源码的朋友,肯定知道TransactionSynchronizationManager这个类,其内部维护了一个ThreadLocal的Map,这里同一线程创建了SqlSession后放入ThreadLocal中,同一线程中其他Mapper接口调用方法时,将会直接从ThreadLocal中获取。

Mybaits 源码解析 (十一)----- 设计模式精妙使用:静态代理和动态代理结合使用:@MapperScan将Mapper接口生成代理注入到Spring的更多相关文章

  1. Mybaits 源码解析 (三)----- Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)

    上一篇我们讲解到mapperElement方法用来解析mapper,我们这篇文章具体来看看mapper.xml的解析过程 mappers配置方式 mappers 标签下有许多 mapper 标签,每一 ...

  2. Mybaits 源码解析 (四)----- SqlSession的创建过程(看懂框架源码再也不用死记硬背面试题)

    SqlSession是mybatis的核心接口之一,是myabtis接口层的主要组成部分,对外提供了mybatis常用的api.myabtis提供了两个SqlSesion接口的实现,常用的实现类是De ...

  3. Mybaits 源码解析 (五)----- 面试源码系列:Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)

    刚开始使用Mybaits的同学有没有这样的疑惑,为什么我们没有编写Mapper的实现类,却能调用Mapper的方法呢?本篇文章我带大家一起来解决这个疑问 上一篇文章我们获取到了DefaultSqlSe ...

  4. Mybaits 源码解析 (九)----- 全网最详细,没有之一:一级缓存和二级缓存源码分析

    像Mybatis.Hibernate这样的ORM框架,封装了JDBC的大部分操作,极大的简化了我们对数据库的操作. 在实际项目中,我们发现在一个事务中查询同样的语句两次的时候,第二次没有进行数据库查询 ...

  5. Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析

    在上一篇文章Mybatis源码解析,一步一步从浅入深(四):将configuration.xml的解析到Configuration对象实例中我们谈到了properties,settings,envir ...

  6. Mybaits 源码解析 (十二)----- Mybatis的事务如何被Spring管理?Mybatis和Spring事务中用的Connection是同一个吗?

    不知道一些同学有没有这种疑问,为什么Mybtis中要配置dataSource,Spring的事务中也要配置dataSource?那么Mybatis和Spring事务中用的Connection是同一个吗 ...

  7. Mybaits 源码解析 (六)----- 全网最详细:Select 语句的执行过程分析(上篇)(Mapper方法是如何调用到XML中的SQL的?)

    上一篇我们分析了Mapper接口代理类的生成,本篇接着分析是如何调用到XML中的SQL 我们回顾一下MapperMethod 的execute方法 public Object execute(SqlS ...

  8. Mybaits 源码解析 (七)----- Select 语句的执行过程分析(下篇)(Mapper方法是如何调用到XML中的SQL的?)全网最详细,没有之一

    我们上篇文章讲到了查询方法里面的doQuery方法,这里面就是调用JDBC的API了,其中的逻辑比较复杂,我们这边文章来讲,先看看我们上篇文章分析的地方 SimpleExecutor public & ...

  9. Mybaits 源码解析 (八)----- 全网最详细,没有之一:结果集 ResultSet 自动映射成实体类对象(上篇)

    上一篇文章我们已经将SQL发送到了数据库,并返回了ResultSet,接下来就是将结果集 ResultSet 自动映射成实体类对象.这样使用者就无需再手动操作结果集,并将数据填充到实体类对象中.这可大 ...

随机推荐

  1. xamarin开发的mac开发小工具集合

    兄弟们我拖控件拖到了mac系统去了, 工具上传到百度网盘,下载地址 链接:https://pan.baidu.com/s/1Q64zoRjE3u66jJnzF8rhww提取码:ljx2 这款工具我是用 ...

  2. Redis持久化的方式有哪些?优缺点分别是什么?

    Redis持久化方式        持久化的目的主要是做灾难恢复,数据恢复.由于Redis的数据全都放在内存里面,如果Redis挂了,没有配置持久化的话,重启的时候数据会全部丢失.         突 ...

  3. Python读取excel 数据

    1.安装xlrd 2.官网 通过官网来查看如何使用python读取Excel,python excel官网: http://www.python-excel.org/ 实例: (1)Excel内容 把 ...

  4. SQL SERVER数据库批量替换某个数据表里的数据update

    批量替换:将A表CMC里面所有包含a替换成b而不影响其他内容UPDATE A SET CMC=REPLACE(CMC,'a','b')

  5. bugku 一个普通的压缩包

    压缩包打不开,用winhex打开压缩包,发现头文件是zip的,将后缀改为zip,成功解压. 打开发现还是个压缩包,而且打不开,提示secret.png损坏,使用winhex打开. 将a8 3c 7a ...

  6. Get https://172.18.255.243:6443/api/v1/namespaces/kube-system/configmaps/kubelet-config-1.12: dial tcp 172.18.255.243:6443: i/o timeout

    问题描述 使用外网加入集群的时候报如下错误: Get https://172.18.255.243:6443/api/v1/namespaces/kube-system/configmaps/kube ...

  7. 基于powershell的socks代理

    0x01 前言 在实战中,内网的代理尤其重要,而常见的端口转发被反病毒软件已经盯死,那么学习使用基于powershell的渗透脚本进行代理.端口转发的非常有必要的. 0x02 使用 介绍ithub: ...

  8. Python之网络编程Socket

    Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法. 高级别的网络 ...

  9. Github 上热门的 Spring Boot 项目实战推荐

    最近经常被读者问到有没有 Spring Boot 实战项目可以学习,于是,我就去 Github 上找了 10 个我觉得还不错的实战项目.对于这些实战项目,有部分是比较适合 Spring Boot 刚入 ...

  10. jmeter打印变量的三种方式

    1.使用Debug Sampler 2.使用log打印到jemter日志 3.使用System.out.println打印到cmd命令行