DataSourceUtils source code analysis

1. Role

Look able to know the name of this class is a wrapper for the DataSource, this class provides a series of tools to operate the database connection method. This class is very important in the Spring affairs, the most important role is to provide the connection bundle can get open when the transaction from the current thread. Spring Jdbc wherein in the JdbcTemplateclass is the use of DataSourceUtils.getConnection()a method of obtaining a connection.

2. TransactionSynchronization

In the analysis DataSourceUtilsbefore the method, briefly explain it at this interface. In fact, for this interface should be very familiar, should be as high frequency in the analysis of the transaction source, the interface appears. In suspend, resume, rollback, commit the transaction methods will trigger this hook interface provided. Here's a look at the definition of the interface

public interface TransactionSynchronization extends Flushable {

    /** Completion status in case of proper commit */
    int STATUS_COMMITTED = 0;

    /** Completion status in case of proper rollback */
    int STATUS_ROLLED_BACK = 1;

    /** Completion status in case of heuristic mixed completion or system errors */
    int STATUS_UNKNOWN = 2;

    /**
     * 此钩子在事务中的suspend方法触发。
     * 这个钩子意味者如果自己在事务运行期间在当前线程绑定过资源,可能需要移除掉。
     * 想象下事务里挂起的时候,会移除掉开启事务时绑定的connectionHolder.
     */
    void suspend();

    /**
     * 与suspend相反,在事务中的resume方法触发
     */
    void resume();

    /**
     * 此钩子在事务提交前执行
     * 在调用commit方法时触发,之后再走commit逻辑。我们知道事务实际的结果还是需要根据
     * commit方法来决定,因为即使调用了commit方法也仍然可能会执行回滚。
     * 如果按照正常流程走提交的话,我们通过这个钩子方法由机会在事务真正提交前还能对数据库做一些操作。
     */
    void beforeCommit(boolean readOnly);

    /**
     * 此钩子在事务完成前执行(发生在beforeCommit之后)
     * 调用commit或者rollback都能触发,此钩子在真正提交或者真正回滚前执行。
     */
    void beforeCompletion();

    /**
     * 事务真正提交后执行,这个钩子执行意味着事务真正提交了。
     */
    void afterCommit();

    /**
     * 事务完成后执行
     * 可能是真正提交了,也有可能是回滚
     */
    void afterCompletion(int status);

}

3. TransactionSynchronizationManager

This class also simply put it down, because the method was very simple code, the role of this class is to bind the resource to the current thread, registered TransactionSynchronization interfaces, bindings each property transaction.

The main point to note is the isSynchronizationActivemethod and isActualTransactionActiveapproach. The former represents the transaction synchronization is active, if activated before TransactionSynchronization interface events can be registered. The latter represents the current thread if there is real business. We know when the previous transaction analysis, Spring opened a Space Affairs isSynchronizationActive() = true, and isActualTransactionActive = falsebecause there is no real transaction.

4. Analytical Method

4.1 Getting connected (getConnection)

Briefly explain the role of this method, the purpose of this method is to get a connection. There are two connection handling behavior

  • This connection is managed by the Spring transaction, whether the transaction is empty or real transaction, as long as this method is the getTransaction()following, in commit/ rollbackbefore the method call. If this transaction is a transaction empty, then this method will get a connection, and will be bound to the current thread, and it will register a ConnectionSynchronization. When the call during a transaction operation commit/ rollbacktime will trigger ConnectionSynchronizationthe beforeCompletionor afterCompletionhook tied to solve connection thread binding, then freed. If this transaction is a real business, then this method is to get the transaction in accordance with the open TransactionDefinition-defined connection with the transaction, the connection will be bound to the current thread, then this method to get from the current thread. This connection with the transaction by the transaction manager unbundling automatically released after the transaction is actually rolled back or committed.
  • Fully managed by the caller, regardless of the Spring transaction, call the method releaseConnection released manually.

Note: The above conclusion is derived from the following code, and comprehensive transaction management code.

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
    try {
        return doGetConnection(dataSource);
    }
    catch (SQLException ex) {
        throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
    }
}
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
    Assert.notNull(dataSource, "No DataSource specified");
    // 从当前线程获取绑定的ConnectionHolder
    ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager
        .getResource(dataSource);
    // 如果不为空,并且内部持有连接,直接返回此连接
    // 首先开启事务绑定的conHolder是肯定持有连接的,那么什么时候conHolder.hasConnection() = false
    // 答案要继续往下找,才能明白。
    // 这里直接解释下: 此方法在一个空事务中会绑定一个connectionHolder,
    // 同时注册了一个ConnectionSynchronization事件,如果这个空事务被挂起,就会触发这个
    // 事件的suspend钩子,这个钩子除了会解绑这个connectionHolder外,还会判断
    // connectionHolder持有的连接是否还被外部程序使用,如果不再使用了,会提前释放掉。
    // 因此如果后面这个事务被恢复了,那么能从数据源中直接获取一个新的连接
    if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
        // 将引用计数加1,代表获取这个conHolder的次数
        conHolder.requested();
        if (!conHolder.hasConnection()) {
            logger.debug("Fetching resumed JDBC Connection from DataSource");
            conHolder.setConnection(dataSource.getConnection());
        }
        return conHolder.getConnection();
    }

    logger.debug("Fetching JDBC Connection from DataSource");
    // 直接从给定的数据源获取连接
    Connection con = dataSource.getConnection();
    // 如果事务同步是开启的,从前面事务源码分析,可以知道如果开启了一个空事务(非事务方式运行),
    // 这个方法的值也会返回true.
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
        logger.debug("Registering transaction synchronization for JDBC Connection");
        // 使用这个连接创建一个ConnectionHolder
        ConnectionHolder holderToUse = conHolder;
        if (holderToUse == null) {
            holderToUse = new ConnectionHolder(con);
        }
        else {
            holderToUse.setConnection(con);
        }
        holderToUse.requested();
        // 注册一个TransactionSynchronization
        // 这里出现一个新类ConnectionSynchronization,看下面解析。
        // 当事务运行整个期间会触发这个类实现的钩子方法
        TransactionSynchronizationManager.registerSynchronization(
            new ConnectionSynchronization(holderToUse, dataSource));
        holderToUse.setSynchronizedWithTransaction(true);
        if (holderToUse != conHolder) {
            // 将这个连接绑定到当前线程
            TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
        }
    }
    return con;
}
// 主要关注这个实现的钩子方法都做了啥
// 首先明确一个观点,由getConnection方法可知,此类被注册前,拿到的连接都是数据源中新鲜的连接
// 也就意味者这个连接是非事务的。
private static class ConnectionSynchronization extends TransactionSynchronizationAdapter {

    private final ConnectionHolder connectionHolder;

    private final DataSource dataSource;

    private int order;

    private boolean holderActive = true;

    public ConnectionSynchronization(ConnectionHolder connectionHolder, 
                                     DataSource dataSource) {
        this.connectionHolder = connectionHolder;
        this.dataSource = dataSource;
        this.order = getConnectionSynchronizationOrder(dataSource);
    }

    @Override
    public int getOrder() {
        return this.order;
    }

    // 当存在一个空事务的时候,getConnection()方法会从数据源中拿到一个资源并绑定到当前线程。
    // 因此当这个空事务被挂起的时候,需要解绑
    // 当外部不存在真实事务时,新建一个新的真实事务,会挂起外部空事务
    // 具体可看AbstractPlatformTransactionManager中getTransaction方法的第二个大分支
    @Override
    public void suspend() {
        if (this.holderActive) {
            // 解绑绑定的连接
            TransactionSynchronizationManager.unbindResource(this.dataSource);
            // 如果这个连接没有再被引用了,也就是说不再使用了,将这个连接提前释放。
            // 这段代码也解决了getConnection方法提的疑问。
            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
    public void resume() {
        // 恢复,与suspend对应
        if (this.holderActive) {
            TransactionSynchronizationManager.bindResource(
                this.dataSource, this.connectionHolder);
        }
    }

    @Override
    public void beforeCompletion() {
        // 如果外部程序不再使用此连接,解绑connectionHolder,并且释放
        // 因为这个连接是非事务的,Spring在调用rollback或者commit不会有实际的事务操作
        // 因此如果不再使用可以提前释放掉
        if (!this.connectionHolder.isOpen()) {
            TransactionSynchronizationManager.unbindResource(this.dataSource);
            this.holderActive = false;
            if (this.connectionHolder.hasConnection()) {
                releaseConnection(this.connectionHolder
                                  .getConnection(), this.dataSource);
            }
        }
    }

    @Override
    public void afterCompletion(int status) {
        // 事务完成后,还没有在beforeCompletion钩子方法中解绑释放掉
        // 那么解绑释放掉
        if (this.holderActive) {
            // The thread-bound ConnectionHolder might not be available anymore,
            // since afterCompletion might get called from a different thread.
            // 这段注释没明白,会什么afterCompletion钩子会被多个线程调用
            // 而beforeCompletion却不会呢? 不应该都是在以一个线程内执行吗?
            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);
            }
        }
        this.connectionHolder.reset();
    }
}

4.2 release connection (releaseConnection)

public static void releaseConnection(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);
    }
}

public static void doReleaseConnection(Connection con, 
                                       DataSource dataSource) throws SQLException {
    if (con == null) {
        return;
    }
    if (dataSource != null) {
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationM
            anager.getResource(dataSource);
        // 此连接受Spring事务管理,可能是一个空事务,也可能是一个真实事务
        if (conHolder != null && connectionEquals(conHolder, con)) {
            // 将此连接引用数减一
            conHolder.released();
            return;
        }
    }
    logger.debug("Returning JDBC Connection to DataSource");
    // 不受Spring事务管理的连接,释放掉,归还给数据源。
    doCloseConnection(con, dataSource);
}

If we can see a direct call this method to release the connection, if the connection from the Spring transaction management, and the connection will not really freed, but this connection reference number minus one, in order to really release this fall connection, you need to have the unbundling of this connection connectinHolder, and then call the method to release times out. Release operation observe the transaction manager, you can also see that after the transaction really committed or rolled back when the connection to be released this fall, will be unbundling first, and then call this method to release the connection.

5. Summary

This paper analyzes the two most important tools in ways other method is very simple, there is nothing to say, the most common of these two methods will, mainly as a supplement Spring transaction management.

Guess you like

Origin www.cnblogs.com/wt20/p/10958238.html