@Resource private MinderDao minderDao; public MindEntity query(){ final MindEntity mindEntity = minderDao.selectOne(1); return mindEntity; }
源码分析
SqlSessionFactoryBean
FactoryBean that creates an MyBatis SqlSessionFactory. This is the usual way to set up a shared MyBatis {@code SqlSessionFactory} in a Spring > >application context; the SqlSessionFactory can then be passed to MyBatis-based DAOs via dependency injection.
if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'"); } } } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found"); } } // 生成SqlSessionFactory returnthis.sqlSessionFactoryBuilder.build(configuration); }
MapperScannerConfigurer
BeanDefinitionRegistryPostProcessor that searches recursively starting from a base package for interfaces and registers them as MapperFactoryBean . Note that only interfaces with at least one method will be registered; concrete classes will be ignored.
if (logger.isDebugEnabled()) { logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); }
// the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean // 注册的是MapperFactoryBean, 构造参数第一个字段是接口类型 definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59 definition.setBeanClass(this.mapperFactoryBean.getClass());
// 添加sqlSessionTemplate属性 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; } elseif (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); } } }
privatefinal PersistenceExceptionTranslator exceptionTranslator; /** * Constructs a Spring managed {@code SqlSession} with the given * {@code SqlSessionFactory} and {@code ExecutorType}. * A custom {@code SQLExceptionTranslator} can be provided as an * argument so any {@code PersistenceException} thrown by MyBatis * can be custom translated to a {@code RuntimeException} * The {@code SQLExceptionTranslator} can also be null and thus no * exception translation will be done and MyBatis exceptions will be * thrown * * @param sqlSessionFactory * @param executorType * @param exceptionTranslator */ publicSqlSessionTemplate(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; // 这里生成了JDK的代理类,实际调用请求被转发给了SqlSessionInterceptor this.sqlSessionProxy = (SqlSession) newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); } /** * {@inheritDoc} */ @Override public <T> T selectOne(String statement){ // 接口的相关方法都转发给代理类 returnthis.sqlSessionProxy.<T> selectOne(statement); } }
// org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor /** * Proxy needed to route MyBatis method calls to the proper SqlSession got * from Spring's Transaction Manager * It also unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to * pass a {@code PersistenceException} to the {@code PersistenceExceptionTranslator}. */ privateclassSqlSessionInterceptorimplementsInvocationHandler{ @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); try { // 反射调用delegate对应的方法 Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() // 非spring管理的,强制commit sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 // 异常时要确保sqlSession被关闭 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; // 翻译成spring定义的标准异常 Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } }
if (LOGGER.isDebugEnabled()) { LOGGER.debug("Creating a new SqlSession"); } // ThreadLocal中为空,就open一个新的session session = sessionFactory.openSession(executorType); // 将新open的session,交给spring来管理 // 会通过TransactionSynchronizationManager,绑定到当前线程的ThreadLocal registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session; }
// org.mybatis.spring.SqlSessionUtils#closeSqlSession /** * Checks if {@code SqlSession} passed as an argument is managed by Spring {@code TransactionSynchronizationManager} * If it is not, it closes it, otherwise it just updates the reference counter and * lets Spring call the close callback when the managed transaction ends * * @param session * @param sessionFactory */ publicstaticvoidcloseSqlSession(SqlSession session, SqlSessionFactory sessionFactory){ notNull(session, NO_SQL_SESSION_SPECIFIED); notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
/** * Callback for cleaning up resources. It cleans TransactionSynchronizationManager and * also commits and closes the {@code SqlSession}. * It assumes that {@code Connection} life cycle will be managed by * {@code DataSourceTransactionManager} or {@code JtaTransactionManager} */ privatestaticfinalclassSqlSessionSynchronizationextendsTransactionSynchronizationAdapter{ /** * {@inheritDoc} */ @Override publicvoidsuspend(){ if (this.holderActive) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Transaction synchronization suspending SqlSession [" + this.holder.getSqlSession() + "]"); } // 清空ThreadLocal,为新事务做准备 TransactionSynchronizationManager.unbindResource(this.sessionFactory); } }
/** * {@inheritDoc} */ @Override publicvoidbeforeCommit(boolean readOnly){ // Connection commit or rollback will be handled by ConnectionSynchronization or // DataSourceTransactionManager. // But, do cleanup the SqlSession / Executor, including flushing BATCH statements so // they are actually executed. // SpringManagedTransaction will no-op the commit over the jdbc connection // TODO This updates 2nd level caches but the tx may be rolledback later on! if (TransactionSynchronizationManager.isActualTransactionActive()) { try { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Transaction synchronization committing SqlSession [" + this.holder.getSqlSession() + "]"); } this.holder.getSqlSession().commit(); } catch (PersistenceException p) { if (this.holder.getPersistenceExceptionTranslator() != null) { DataAccessException translated = this.holder .getPersistenceExceptionTranslator() .translateExceptionIfPossible(p); if (translated != null) { throw translated; } } throw p; } } }
/** * {@inheritDoc} */ @Override publicvoidbeforeCompletion(){ // Issue #18 Close SqlSession and deregister it now // because afterCompletion may be called from a different thread if (!this.holder.isOpen()) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]"); } TransactionSynchronizationManager.unbindResource(sessionFactory); this.holderActive = false; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]"); } this.holder.getSqlSession().close(); } }
/** * {@inheritDoc} */ @Override publicvoidafterCompletion(int status){ if (this.holderActive) { // afterCompletion may have been called from a different thread // so avoid failing if there is nothing in this one if (LOGGER.isDebugEnabled()) { LOGGER.debug("Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]"); } TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory); this.holderActive = false; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]"); } this.holder.getSqlSession().close(); } this.holder.reset(); } } }
SpringManagedTransaction
SpringManagedTransaction handles the lifecycle of a JDBC connection. It retrieves a connection from Spring’s transaction manager and returns it back to it when it is no longer needed. If Spring’s transaction handling is active it will no-op all commit/rollback/close calls assuming that the Spring transaction manager will do the job. If it is not it will behave like JdbcTransaction.
publicclassSpringManagedTransactionimplementsTransaction{ /** * {@inheritDoc} */ @Override public Connection getConnection()throws SQLException { if (this.connection == null) { openConnection(); } returnthis.connection; } /** * Gets a connection from Spring transaction manager and discovers if this * {@code Transaction} should manage connection or let it to Spring. * <p> * It also reads autocommit setting because when using Spring Transaction MyBatis * thinks that autocommit is always false and will always call commit/rollback * so we need to no-op that calls. */ privatevoidopenConnection()throws SQLException { // 委托给了spring的工具类来获取连接 this.connection = DataSourceUtils.getConnection(this.dataSource); this.autoCommit = this.connection.getAutoCommit(); this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
/** * Actually obtain a JDBC Connection from the given DataSource. * Same as {@link #getConnection}, but throwing the original SQLException. * <p>Is aware of a corresponding Connection bound to the current thread, for example * when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread * if transaction synchronization is active (e.g. if in a JTA transaction). * <p>Directly accessed by {@link TransactionAwareDataSourceProxy}. * @param dataSource the DataSource to obtain Connections from * @return a JDBC Connection from the given DataSource * @throws SQLException if thrown by JDBC methods * @see #doReleaseConnection */ publicstatic Connection doGetConnection(DataSource dataSource)throws SQLException { Assert.notNull(dataSource, "No DataSource specified"); // 从ThreadLocal中去获取ConnectionHolder ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) { // 引用计数+1 conHolder.requested(); if (!conHolder.hasConnection()) { logger.debug("Fetching resumed JDBC Connection from DataSource"); conHolder.setConnection(dataSource.getConnection()); } return conHolder.getConnection(); } // Else we either got no holder or an empty thread-bound holder here. // 获取连接 logger.debug("Fetching JDBC Connection from DataSource"); Connection con = dataSource.getConnection(); // 事务的场景下,才会复用连接 if (TransactionSynchronizationManager.isSynchronizationActive()) { // 第一次进来的时候ThreadLocal里是没有ConnectionHolder的,这里需要获取连接,然后注册给spring管理 logger.debug("Registering transaction synchronization for JDBC Connection"); // Use same Connection for further JDBC actions within the transaction. // Thread-bound object will get removed by synchronization at transaction completion. ConnectionHolder holderToUse = conHolder; if (holderToUse == null) { holderToUse = new ConnectionHolder(con); } else { holderToUse.setConnection(con); } // 引用计数+1 // referenceCount++ holderToUse.requested(); // 注册回调处理函数,事务状态发生变化时,会处理connHolder TransactionSynchronizationManager.registerSynchronization( new ConnectionSynchronization(holderToUse, dataSource)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != conHolder) { // 绑定到ThreadLocal TransactionSynchronizationManager.bindResource(dataSource, holderToUse); } } return con; }
// org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection /** * Close the given Connection, obtained from the given DataSource, * if it is not managed externally (that is, not bound to the thread). * @param con the Connection to close if necessary * (if this is {@code null}, the call will be ignored) * @param dataSource the DataSource that the Connection was obtained from * (may be {@code null}) * @see #getConnection */ publicstaticvoidreleaseConnection(Connection con, DataSource dataSource){ try { doReleaseConnection(con, dataSource); } catch (SQLException ex) { logger.debug("Could not close JDBC Connection", ex); } catch (Throwable ex) { logger.debug("Unexpected exception on closing JDBC Connection", ex); } }
publicstaticvoiddoReleaseConnection(Connection con, DataSource dataSource)throws SQLException { if (con == null) { return; } if (dataSource != null) { ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (conHolder != null && connectionEquals(conHolder, con)) { // It's the transactional Connection: Don't close it. // 事务连接,这里只是减少计数,实际的释放在事务完成之后,在ConnectionSynchronization中处理的 // referenceCount-- conHolder.released(); return; } } logger.debug("Returning JDBC Connection to DataSource"); // 非事务连接,直接关闭 doCloseConnection(con, dataSource); }
// org.springframework.jdbc.datasource.DataSourceUtils.ConnectionSynchronization /** * Callback for resource cleanup at the end of a non-native JDBC transaction * (e.g. when participating in a JtaTransactionManager transaction). * @see org.springframework.transaction.jta.JtaTransactionManager */ privatestaticclassConnectionSynchronizationextendsTransactionSynchronizationAdapter{
@Override publicvoidsuspend(){ // 比如事务的传播行为是REQUEST_NEW,每次都会创建一个新连接, // 会挂起当前的事务,当事务挂起的时候,就会回调到这里 // conn在ThreadLocal中的缓存形式是 dataSource -> conn // 所以这里要先把之前的conn给unbind掉,新的连接才能正常的工作 if (this.holderActive) { // dataSource -> conn 清除上一个事务对应的连接 TransactionSynchronizationManager.unbindResource(this.dataSource); // isOpen就是看引用技术是否大于0,如果大于0标识还有人在用,这里不会关闭 if (this.connectionHolder.hasConnection() && !this.connectionHolder.isOpen()) { // Release Connection on suspend if the application doesn't keep // a handle to it anymore. We will fetch a fresh Connection if the // application accesses the ConnectionHolder again after resume, // assuming that it will participate in the same transaction. releaseConnection(this.connectionHolder.getConnection(), this.dataSource); this.connectionHolder.setConnection(null); } } }
@Override publicvoidbeforeCompletion(){ // Release Connection early if the holder is not open anymore // (that is, not used by another resource like a Hibernate Session // that has its own cleanup via transaction synchronization), // to avoid issues with strict JTA implementations that expect // the close call before transaction completion. // 事务完成之前 // 这个阶段如果conn已经没有引用了,就直接关闭 if (!this.connectionHolder.isOpen()) { TransactionSynchronizationManager.unbindResource(this.dataSource); this.holderActive = false; if (this.connectionHolder.hasConnection()) { releaseConnection(this.connectionHolder.getConnection(), this.dataSource); } } }
@Override publicvoidafterCompletion(int status){ // If we haven't closed the Connection in beforeCompletion, // close it now. The holder might have been used for other // cleanup in the meantime, for example by a Hibernate Session. if (this.holderActive) { // The thread-bound ConnectionHolder might not be available anymore, // since afterCompletion might get called from a different thread. TransactionSynchronizationManager.unbindResourceIfPossible(this.dataSource); // 置为非活跃 this.holderActive = false; if (this.connectionHolder.hasConnection()) { releaseConnection(this.connectionHolder.getConnection(), this.dataSource); // Reset the ConnectionHolder: It might remain bound to the thread. this.connectionHolder.setConnection(null); } } // 清空当前holder的状态 this.connectionHolder.reset(); } }