1. 程式人生 > >Mybaits 原始碼解析 (十一)----- 設計模式精妙使用:靜態代理和動態代理結合使用:@MapperScan將Mapper介面生成代理注入到Spring

Mybaits 原始碼解析 (十一)----- 設計模式精妙使用:靜態代理和動態代理結合使用:@MapperScan將Mapper介面生成代理注入到Spring

上一篇文章我們講了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使用@ImportMapperScannerRegistrar匯入。

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

 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做一些額外的處理。

processBeanDefinitions

上面BeanDefinition已經注入進Spring容器了,接著我們看對BeanDefinition進行哪些額外的處理

 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時,這個類的過程:

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中獲取。