美文网首页
Mybatis SqlSessionTemplate 实例化过程

Mybatis SqlSessionTemplate 实例化过程

作者: 风吟空城 | 来源:发表于2019-04-10 14:54 被阅读0次

    构造方法

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

    sqlSessionProxy

    从构造方法中能够看出sqlSessionProxySqlSessionTemplate执行CRUD操作时的实际执行者。所以重点解析下sqlSessionProxy的实例化过程。不难看出,sqlSessionProxy实例化使用了java的动态代理设计模式。传入的参数如下:

    • ClassLoaderSqlSessionFactory
    • 接口是SqlSession
    • 调用处理程序是SqlSessionTemplate的内部类SqlSessionInterceptor

    接下来看看SqlSessionInterceptor都做了什么。

    SqlSessionInterceptor

        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) {
                        Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                        if (translated != null) {
                            unwrapped = translated;
                        }
                    }
    
                    throw (Throwable)unwrapped;
                } finally {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }
    
                return unwrapped;
            }
        }
    

    SqlSessionInterceptor的构造方法并没有任何参数,即没有指明SqlSession的实际实现类。这是因为SqlSession的实际实现类在invoke()中进行了实例化。即SqlSessionUtilsgetSqlSession()方法参与生成SqlSession的实际实现类。

    SqlSessionUtils getSqlSession

    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
            Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
            Assert.notNull(executorType, "No ExecutorType specified");
            SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
            if (holder != null && holder.isSynchronizedWithTransaction()) {
                if (holder.getExecutorType() != executorType) {
                    throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
                } else {
                    ...
                    return holder.getSqlSession();
                }
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Creating a new SqlSession");
                }
    
                SqlSession session = sessionFactory.openSession(executorType);
                ...          
                return session;
            }
        }
    

    getSqlSession方法分为两部分。

    1. 判断SqlSessionHolder在内存中是否存在,如果存在则直接从内存中取出,通过sqlSessionholder.getSqlSession()获取SqlSession的实际实现类。
    2. 如果不存在,则先创建SqlSession,然后再创建SqlSessionHolder,最后放入内存方便下次直接使用。

    下面我们看看是如何生成SqlSession的。

    SqlSession session = sessionFactory.openSession(executorType);
    

    在通过sessionFactory创建SqlSession时。要搞清楚sessionFactory是在application.xml配置Mybatis时传入的。即:

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
    </bean>
    

    需要注意的是SqlSessionFactoryBean实现了SpringFactoryBean接口。这意味着由Spring最终创建的 bean并不是SqlSessionFactoryBean本身,而是工厂类(SqlSessionFactoryBean)的getObject()方法的返回结果。这种情况下,Spring 将会在应用启动时为你创建SqlSessionFactory并使用sqlSessionFactory这个名字存储起来。关于FactoryBean这里不再详细解释。

    查看SqlSessionFactoryBeangetObject()方法,然后顺着内部方法一层一层查看。最终发现发现返回的SqlSessionFactory其实是DefaultSqlSessionFactory类。路径:afterPropertiesSet方法 -->buildSqlSessionFactory方法 --> build()方法。代码路径如下:

    SqlSessionFactoryBean

        public SqlSessionFactory getObject() throws Exception {
            if (this.sqlSessionFactory == null) {
                this.afterPropertiesSet();
            }
    
            return this.sqlSessionFactory;
        }
    

    afterPropertiesSet

        public void afterPropertiesSet() throws Exception {
            Assert.notNull(this.dataSource, "Property 'dataSource' is required");
            Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
            this.sqlSessionFactory = this.buildSqlSessionFactory();
        }
    

    buildSqlSessionFactory

        protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
            XMLConfigBuilder xmlConfigBuilder = null;
            Configuration configuration;
            ...
            ...
            return this.sqlSessionFactoryBuilder.build(configuration);
        }
    

    SqlSessionFactoryBuilder

        public SqlSessionFactory build(Configuration config) {
            return new DefaultSqlSessionFactory(config);
        }
    

    既然传入的sessionFactory参数是DefaultSqlSessionFactory,那我们就可以查看DefaultSqlSessionFactoryopenSession方法了。即sqlSessionProxyDefaultSqlSession实例。

    DefaultSqlSessionFactory

        public SqlSession openSession(ExecutorType execType) {
            return this.openSessionFromDataSource(execType, (TransactionIsolationLevel)null, false);
        }
    
        ...
    
        private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
            Transaction tx = null;
    
            DefaultSqlSession var8;
            try {
                ...
                var8 = new DefaultSqlSession(this.configuration, executor);
            } catch (Exception var12) {
                this.closeTransaction(tx);
                throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
            } finally {
                ErrorContext.instance().reset();
            }
    
            return var8;
        }
    

    总结

    SqlSessionTemplate的实例化过程主要利用了java的动态代理设计模式和FactoryBean<T>自定义工厂这两个知识点。

    • SqlSessionTemplate 是代理类;
    • SqlSession 是接口;
    • DefaultSqlSession 是实际实现类;

    相关文章

      网友评论

          本文标题:Mybatis SqlSessionTemplate 实例化过程

          本文链接:https://www.haomeiwen.com/subject/ihqniqtx.html