最近分析mybatis的mapper时,研究源码时,这些概念理清如下:
- sqlSessionFactoryBean,主要作用是通过getObject得到sqlSessionFactory,同时可以设置数据源,mybatis基本配置等。
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private Configuration configuration;
private Resource[] mapperLocations;
private DataSource dataSource;
private TransactionFactory transactionFactory;
private Properties configurationProperties;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
private SqlSessionFactory sqlSessionFactory;
//...
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
}
- sqlSessionFactory,用于创建sqlSession的工厂方法。
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
//...
}
- sqlSession,执行sql命令的会话。
public interface SqlSession extends Closeable {
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement);
//...
}
- SqlSessionTemplate是sqlSession的实现类,是线程安全的。里面有一个动态代理的SqlSession sqlSessionProxy;代理sqlSessionProxy执行invoke方法的时候,每次invoke方法都是新生成一个SqlSession来执行,这样就保持了线程安全。
还有一个点,getMapper()方法是得到一个mapper接口的代理对象,且会传入this即本sqlSessionTemplate,最终调用sql语句的时候还是使用代理的sqlSessionProxy(实际还是invoke的新的SqlSession)。【记住,2个动态代理】
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;
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());//【注意这个sqlSessionProxy ,和SqlSessionInterceptor的invoke方法】
}
//获取代理mapper对象,且一直传入this即本sqlSessionTemplate对象,执行sql语句的时候使用的代理sqlSessionProxy,它又使用invoke方法的新的sqlSession
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
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);
//...
}
//...
}
- SqlSessionManager是sqlSession的实现类,是线程安全的。里面也有一个动态代理的SqlSession sqlSessionProxy;同时维护一个ThreadLocal localSqlSession,所以代理sqlSessionProxy执行invoke方法的时候,就是拿各自线程的ThreadLocal的SqlSession。
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}
//也是代理
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
private class SqlSessionInterceptor implements InvocationHandler {
public SqlSessionInterceptor() {
// Prevent Synthetic Access
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
//...
}
}
}
-
DefaultSqlSession是sqlSession的实现类,普通的一次请求,不安全的。
-
MapperFactoryBean,创建mapper的工厂类,getObject()得到mapper接口的动态代理生成的代理类,它继承SqlSessionDaoSupport来间接操作SqlSessionTemplate。即getObject()->getMapper()
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;//这就是实际的mapper接口
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
}
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
}
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
- @MapperScan,会扫描目标目录的所有Mapper接口,并名字定义成mapper接口名,但是class类名是MapperFactoryBean,里面的有一个属性是mapper的权限名。如此启动项目,加载单例bean到上下文容器的时候,调用getObject会调用SqlSessionTemplate的getMapper(),得到代理mapper对象。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)//关注MapperScannerRegistrar
@Repeatable(MapperScans.class)
public @interface MapperScan {
}
scanner.doScan(StringUtils.toStringArray(basePackages));
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());
//注解版
<bean id="roleMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.qsm.dao.RoleMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
- @Mapper,若没有使用@MapperScan【
@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
】,而使用@Mapper,原理也类似,会扫描主启动类目录下的所有标注了@Mapper的mapper接口文件
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
@org.springframework.context.annotation.Configuration
@Import(AutoConfiguredMapperScannerRegistrar.class)
@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
@Override
public void afterPropertiesSet() {
logger.debug(
"Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
}
}
}
若仔细理解上面的知识点,则下面的题目就应该知道答案了。
1、Mybatis的mapper接口文件原理是什么
2、SqlSessionTemplate是如何保持线程安全的
3、SqlSessionTemplate与SqlSessionManager区别
4、@MapperScan和@Mapper从源码角度是怎么工作的
知道的小伙伴也可以评论一下答案,也许就能帮助其他小伙伴哦
【完,喜欢就点个赞呗】
正在去往BAT的路上修行