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

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

扫描Mapper接口

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

方式一:数据库

<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使用@ImportMapperScannerRegistrar导入。session

MapperScannerRegistrar

 1 public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
 2   private ResourceLoader resourceLoader;
 3   @Override
 4   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
 5     // 获取MapperScan 注解,如@MapperScan("com.chenhao.mapper")
 6     AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));  7     // 建立路径扫描器,下面的一大段都是将MapperScan 中的属性设置到ClassPathMapperScanner ,作的就是一个set操做
 8     ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);  9     // this check is needed in Spring 3.1
10     if (resourceLoader != null) {
11       // 设置资源加载器,做用:扫描指定包下的class文件。
12       scanner.setResourceLoader(resourceLoader);
13     }
14     Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
15     if (!Annotation.class.equals(annotationClass)) {
16       scanner.setAnnotationClass(annotationClass);
17     }
18     Class<?> markerInterface = annoAttrs.getClass("markerInterface");
19     if (!Class.class.equals(markerInterface)) {
20       scanner.setMarkerInterface(markerInterface);
21     }
22     Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
23     if (!BeanNameGenerator.class.equals(generatorClass)) {
24       scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
25     }
26     Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
27     if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
28       scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
29     }
30     scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
31     //设置SqlSessionFactory的名称
32     scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef")); 33     List<String> basePackages = new ArrayList<String>(); 34     //获取配置的包路径,如com.chenhao.mapper
35     for (String pkg : annoAttrs.getStringArray("value")) { 36       if (StringUtils.hasText(pkg)) { 37  basePackages.add(pkg); 38  } 39  } 40     for (String pkg : annoAttrs.getStringArray("basePackages")) {
41       if (StringUtils.hasText(pkg)) {
42         basePackages.add(pkg);
43       }
44     }
45     for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
46       basePackages.add(ClassUtils.getPackageName(clazz));
47     }
48     // 注册过滤器,做用:什么类型的Mapper将会留下来。
49     scanner.registerFilters();
50     // 扫描指定包
51  scanner.doScan(StringUtils.toStringArray(basePackages)); 52   }
53 }

ClassPathMapperScanner

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

 1 @Override
 2 public Set<BeanDefinitionHolder> doScan(String... basePackages) {
 3     //调用父类进行扫描,并将basePackages下的class都封装成BeanDefinitionHolder,并注册进Spring容器的BeanDefinition
 4     Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
 5  
 6     if (beanDefinitions.isEmpty()) {
 7       logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
 8     } else {
 9       //继续对beanDefinitions作处理,额外设置一些属性
10       processBeanDefinitions(beanDefinitions);
11     }
12  
13     return beanDefinitions;
14 }
15   
16 protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
17     Assert.notEmpty(basePackages, "At least one base package must be specified");
18     Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
19     //遍历basePackages进行扫描
20     for (String basePackage : basePackages) {
21         //找出匹配的类
22         Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
23         for (BeanDefinition candidate : candidates) {
24             ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
25             candidate.setScope(scopeMetadata.getScopeName());
26             String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
27             if (candidate instanceof AbstractBeanDefinition) {
28                 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
29             }
30             if (candidate instanceof AnnotatedBeanDefinition) {
31                 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
32             }
33             if (checkCandidate(beanName, candidate)) {
34                 //封装成BeanDefinitionHolder 对象
35                 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
36                 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
37                 beanDefinitions.add(definitionHolder);
38                 //将BeanDefinition对象注入spring的BeanDefinitionMap中,后续getBean时,就是从BeanDefinitionMap获取对应的BeanDefinition对象,取出其属性进行实例化Bean
39                 registerBeanDefinition(definitionHolder, this.registry);
40             }
41         }
42     }
43     return beanDefinitions;
44 }

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

processBeanDefinitions

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

 1 private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
 2     GenericBeanDefinition definition;
 3     for (BeanDefinitionHolder holder : beanDefinitions) {
 4       definition = (GenericBeanDefinition) holder.getBeanDefinition();
 5  
 6       if (logger.isDebugEnabled()) {
 7         logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
 8           + "' and '" + definition.getBeanClassName() + "' mapperInterface");
 9       }
10  
11       // 设置definition构造器的输入参数为definition.getBeanClassName(),这里就是com.chenhao.mapper.UserMapper
12       // 那么在getBean实例化时,经过反射调用构造器实例化时要将这个参数传进去
13       definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName())
14       // 修改definition对应的Class
15       // 看过Spring源码的都知道,getBean()返回的就是BeanDefinitionHolder中beanClass属性对应的实例
16       // 因此咱们后面ac.getBean(UserMapper.class)的返回值也就是MapperFactoryBean的实例
17       // 可是最终被注入到Spring容器的对象的并非MapperFactoryBean的实例,根据名字看,咱们就知道MapperFactoryBean实现了FactoryBean接口
18       // 那么最终注入Spring容器的一定是从MapperFactoryBean的实例的getObject()方法中返回
19       definition.setBeanClass(this.mapperFactoryBean.getClass());
20  
21       definition.getPropertyValues().add("addToConfig", this.addToConfig);
22  
23       boolean explicitFactoryUsed = false;
24       if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
25         definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
26         explicitFactoryUsed = true;
27       } else if (this.sqlSessionFactory != null) {
28         //往definition设置属性值sqlSessionFactory,那么在MapperFactoryBean实例化后,进行属性赋值时populateBean(beanName, mbd, instanceWrapper);,会经过反射调用sqlSessionFactory的set方法进行赋值
29         //也就是在MapperFactoryBean建立实例后,要调用setSqlSessionFactory方法将sqlSessionFactory注入进MapperFactoryBean实例
30         definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
31         explicitFactoryUsed = true;
32       }
33  
34       if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
35         if (explicitFactoryUsed) {
36           logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
37         }
38         definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
39         explicitFactoryUsed = true;
40       } else if (this.sqlSessionTemplate != null) {
41         if (explicitFactoryUsed) {
42           logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
43         }
44         definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
45         explicitFactoryUsed = true;
46       }
47  
48       if (!explicitFactoryUsed) {
49         if (logger.isDebugEnabled()) {
50           logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
51         }
52         definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
53       }
54     }
55 }

看第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时,这个类的过程:

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

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

三、因为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代理类调用方法执行逻辑:

一、SqlSessionTemplate生成Mapper代理类时,将自己传进去作为Mapper代理类的属性,调用Mapper代理类的方法时,最后会经过SqlSession类执行,也就是调用SqlSessionTemplate中的方法。

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

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

四、若是当前操做并无在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中获取。

相关文章
相关标签/搜索