c3p0 探索计划

1.简单来个c3p0的代码

1.1 添加依赖

    
        c3p0
        c3p0
        0.9.1.2
    

    
        mysql
        mysql-connector-java
        5.1.6
    

1.2 写一个简单实例(可以参考:c3p0 官网)

public class C3p0DemoTest {

private String driver = "com.mysql.jdbc.Driver";
private String url = "jdbc:mysql://192.168.80.4:3306/MyTestDb?useUnicode=true&characterEncoding=UTF-8";
private String user = "root";
private String password = "xm123456";

@Test
public void test1() {
    ComboPooledDataSource cpds = new ComboPooledDataSource();
    try {
        cpds.setDriverClass(driver);
        cpds.setJdbcUrl(url);
        cpds.setUser(user);
        cpds.setPassword(password);
    } catch (PropertyVetoException e) {
        e.printStackTrace();
    }
    try {
        Connection connection = cpds.getConnection();
        connection.close();
        connection = cpds.getConnection();
        connection.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }

}
}

2 分析下ComboPooledDataSource的初始化

2.1 ComboPooledDataSource

public ComboPooledDataSource()
{ this( true ); }

public ComboPooledDataSource( boolean autoregister )
{
    super( autoregister );

    // System.err.println("...Initializing ComboPooledDataSource.");

    dmds  = new DriverManagerDataSource();
    wcpds = new WrapperConnectionPoolDataSource();

    wcpds.setNestedDataSource( dmds );

    try
    { this.setConnectionPoolDataSource( wcpds ); }
    catch (PropertyVetoException e)
    {
        logger.log(MLevel.WARNING, "Hunh??? This can't happen. We haven't set up any listeners to veto the property change yet!", e);
        throw new RuntimeException("Hunh??? This can't happen. We haven't set up any listeners to veto the property change yet! " + e);
    }

    // set things up in case there are future changes to our ConnectionPoolDataSource
    //
    setUpPropertyEvents();
}

简单理解下上面的代码:
1.首先调用父类的构造器
2.实例化DriverManagerDataSource,这个类的作用就是生成原生的Connection
3.实例化WrapperConnectionPoolDataSource,这个类的作用就是生成NewPooledConnection,这个对象是c3p0数据连接池里真正维护的对象,ConnectionNewPooledConnection的一个属性
4.WrapperConnectionPoolDataSource绑定DriverManagerDataSource
5.注册监听器,配置变化会被监听并刷新数据,换句话理解就是,同一个配置属性发生了变化,会及时刷新
ComboPooledDataSource 就是一个对外暴漏的服务,两个作用,一个是提供连接,一个是加载配置

2.2 父类AbstractPoolBackedDataSource初始化

protected AbstractPoolBackedDataSource( boolean autoregister )
{
    super( autoregister );
    setUpPropertyEvents();
}

2.3 父类PoolBackedDataSourceBase

public PoolBackedDataSourceBase(boolean autoregister) {
    if (autoregister) {
        this.identityToken = C3P0ImplUtils.allocateIdentityToken(this);
        C3P0Registry.reregister(this);
    }
}

代码很好理解:
1.获得token
2.注册数据源

3.获取Connection的过程

获取Connection的过程

3.1 解释下这几个类的作用

作用
AbstractPoolBackedDataSource 维护了一个连接池管理者C3P0PooledConnectionPoolManager
C3P0PooledConnectionPoolManager 维护了一个数据源连接池C3P0PooledConnectionPoolMap集合
C3P0PooledConnectionPool 数据源连接池,但数据源并未存放在这里,只是负责获得和释放连接,维护了一个BasicResourcePool资源池
BasicResourcePool 资源池,里面维护者真正的连接资源NewPooledConnection
NewPooledConnection 并未真正实现Connection
NewProxyConnection 用户真正用到的连接

3.2 debug走起看风景

1.进入方法



2.进入父类

public Connection getConnection() throws SQLException
{
    PooledConnection pc = getPoolManager().getPool().checkoutPooledConnection();
    return pc.getConnection();
}

3.获取PoolMannager

private synchronized C3P0PooledConnectionPoolManager getPoolManager() throws SQLException
{
    if (poolManager == null)
    {
        ConnectionPoolDataSource cpds = assertCpds();
        poolManager = new C3P0PooledConnectionPoolManager(cpds, null, null, this.getNumHelperThreads(), this.getIdentityToken());
        if (logger.isLoggable(MLevel.INFO))
            logger.info("Initializing c3p0 pool... " + this.toString()  /* + "; using pool manager: " + poolManager */);
    }
    return poolManager;     
}

4.获取Pool

public C3P0PooledConnectionPool getPool()
throws SQLException
{ return getPool( defaultAuth ); }

public synchronized C3P0PooledConnectionPool getPool(DbAuth auth)
throws SQLException
{
    C3P0PooledConnectionPool out = (C3P0PooledConnectionPool) authsToPools.get(auth);
    if (out == null)
    {
        out = createPooledConnectionPool(auth);
        authsToPools.put( auth, out );
    }
    return out;
}

这里的out就是个map集合

public PooledConnection checkoutPooledConnection() throws SQLException
{ 
    //System.err.println(this + " -- CHECKOUT");
    try { return (PooledConnection) rp.checkoutResource( checkoutTimeout ); }
    catch (TimeoutException e)
    { throw SqlUtils.toSQLException("An attempt by a client to checkout a Connection has timed out.", e); }
    catch (CannotAcquireResourceException e)
    { throw SqlUtils.toSQLException("Connections could not be acquired from the underlying database!", "08001", e); }
    catch (Exception e)
    { throw SqlUtils.toSQLException(e); }
}

这里的rpBasicResourcePool
5.取出NewPooledConnection连接

public Object checkoutResource( long timeout )
throws TimeoutException, ResourcePoolException, InterruptedException
{
    Object resc = prelimCheckoutResource( timeout );

    boolean refurb = attemptRefurbishResourceOnCheckout( resc );

    synchronized( this )
    {
        if (!refurb)
        {
            removeResource( resc );
            ensureMinResources();
            resc = null;
        }
        else
        {
            asyncFireResourceCheckedOut( resc, managed.size(), unused.size(), excluded.size() );
            if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) trace();

            PunchCard card = (PunchCard) managed.get( resc );
            if (card == null) //the resource has been removed!
            {
                if (logger.isLoggable( MLevel.FINE ))
                    logger.fine("Resource " + resc + " was removed from the pool while it was being checked out " +
                    " or refurbished for checkout.");
                resc = null;
            }
            else
            {
                card.checkout_time = System.currentTimeMillis();
                if (debug_store_checkout_exceptions)
                    card.checkoutStackTraceException = new Exception("DEBUG ONLY: Overdue resource check-out stack trace.");
            }
        }
    }

    // best to do the recheckout while we don't hold this'
    // lock, so we don't refurbish-on-checkout while holding.
    if (resc == null)
        return checkoutResource( timeout );
    else
        return resc;
}

private synchronized Object prelimCheckoutResource( long timeout )
throws TimeoutException, ResourcePoolException, InterruptedException
{
    try
    {
        ensureNotBroken();

        int available = unused.size();
        if (available == 0)
        {
            int msz = managed.size();

            if (msz < max)
            {
                // to cover all the load, we need the current size, plus those waiting already for acquisition, 
                // plus the current client 
                int desired_target = msz + acquireWaiters.size() + 1;

                if (logger.isLoggable(MLevel.FINER))
                    logger.log(MLevel.FINER, "acquire test -- pool size: " + msz + "; target_pool_size: " + target_pool_size + "; desired target? " + desired_target);

                if (desired_target >= target_pool_size)
                {
                    //make sure we don't grab less than inc Connections at a time, if we can help it.
                    desired_target = Math.max(desired_target, target_pool_size + inc);

                    //make sure our target is within its bounds
                    target_pool_size = Math.max( Math.min( max, desired_target ), min );

                    _recheckResizePool();
                }
            }
            else
            {
                if (logger.isLoggable(MLevel.FINER))
                    logger.log(MLevel.FINER, "acquire test -- pool is already maxed out. [managed: " + msz + "; max: " + max + "]");
            }

            awaitAvailable(timeout); //throws timeout exception
        }

        Object  resc = unused.get(0);

        // this is a hack -- but "doing it right" adds a lot of complexity, and collisions between
        // an idle check and a checkout should be relatively rare. anyway, it should work just fine.
        if ( idleCheckResources.contains( resc ) )
        {
            if (Debug.DEBUG && logger.isLoggable( MLevel.FINER))
                logger.log( MLevel.FINER, 
                                "Resource we want to check out is in idleCheck! (waiting until idle-check completes.) [" + this + "]");

            // we'll move remove() to after the if, so we don't have to add back
            // unused.add(0, resc );

            // we'll wait for "something to happen" -- probably an idle check to
            // complete -- then we'll try again and hope for the best.
            Thread t = Thread.currentThread();
            try
            {
                otherWaiters.add ( t );
                this.wait( timeout );
                ensureNotBroken();
            }
            finally
            { otherWaiters.remove( t ); }
            return prelimCheckoutResource( timeout );
        }
        else if ( shouldExpire( resc ) )
        {
            removeResource( resc );
            ensureMinResources();
            return prelimCheckoutResource( timeout );
        }
        else
        {
            unused.remove(0);
            return resc;
        }
    }
    catch ( ResourceClosedException e ) // one of our async threads died
    {
        //System.err.println(this + " -- the pool was found to be closed or broken during an attempt to check out a resource.");
        //e.printStackTrace();
        if (logger.isLoggable( MLevel.SEVERE ))
            logger.log( MLevel.SEVERE, this + " -- the pool was found to be closed or broken during an attempt to check out a resource.", e );

        this.unexpectedBreak();
        throw e;
    }
    catch ( InterruptedException e )
    {
        //      System.err.println(this + " -- an attempt to checkout a resource was interrupted: some other thread " +
        //                 "must have either interrupted the Thread attempting checkout, or close() was called on the pool.");
        //      e.printStackTrace();
        if (broken)
        {
            if (logger.isLoggable( MLevel.FINER ))
                logger.log(MLevel.FINER, 
                                this + " -- an attempt to checkout a resource was interrupted, because the pool is now closed. " +
                                "[Thread: " + Thread.currentThread().getName() + ']',
                                e );
            else if (logger.isLoggable( MLevel.INFO ))
                logger.log(MLevel.INFO, 
                                this + " -- an attempt to checkout a resource was interrupted, because the pool is now closed. " +
                                "[Thread: " + Thread.currentThread().getName() + ']');
        }
        else
        {
            if (logger.isLoggable( MLevel.WARNING ))
            {
                logger.log(MLevel.WARNING, 
                                this + " -- an attempt to checkout a resource was interrupted, and the pool is still live: some other thread " +
                                "must have either interrupted the Thread attempting checkout!",
                                e );
            }
        }
        throw e;
    }
}

这里ok,属于整个c3p0的核心了,对整个资源的调度策略都在这里,但不是我们关注的重点
Object resc = unused.get(0) 才是核心
6.取出NewProxyConnection,返回结束

public synchronized Connection getConnection() throws SQLException
{
    try
    {
        //throw new SQLException("NOT IMPLEMENTED");
        if ( exposedProxy == null )
        {
            exposedProxy = new NewProxyConnection( physicalConnection, this );
        }
        else
        {
            if ( logger.isLoggable( MLevel.WARNING ) )
                logger.warning("c3p0 -- Uh oh... getConnection() was called on a PooledConnection when " +
                                "it had already provided a client with a Connection that has not yet been " +
                "closed. This probably indicates a bug in the connection pool!!!");

        }
        return exposedProxy;
    }
    catch ( Exception e )
    {
        SQLException sqle = handleThrowable( e );
        throw sqle;
    }
}

exposedProxy = new NewProxyConnection( physicalConnection, this )
这句代码才是核心,就是把原生的ConnectionNewPooledConnection都给NewProxyConnection

NewProxyConnection(Connection inner, NewPooledConnection parentPooledConnection) {
    this(inner);
    this.attach(parentPooledConnection);
}

void attach(NewPooledConnection parentPooledConnection) {
    this.parentPooledConnection = parentPooledConnection;
    parentPooledConnection.addConnectionEventListener(this.cel);
}

更加简单,就是把这两个属性绑定一下,然后注册监听器,负责管理资源(连接使用超时自动回收)

3.3 总结

其实整个流程还是很明朗的。
数据源里没有连接,但有连接池管理器。
连接是放在资源池里,资源池放在连接池里,连接池直接被连接池管理器管理,好的,拿到了连接池去可以取到连接。
但是取到的连接不符合规矩(没有实现Connection接口),怎么办,把它再封装一次,成为符合规矩的。简单理解就是代理,老子不行儿子顶上。


4 c3p0懒加载的真正意义

以上所有的过程,在获取连接池管理器、连接池、资源池、资源的时候,发现都已经完成实例化了。
但我们为什么毫无感觉,毕竟在初始化操作里没有对这些进行初始化。
那么,这些类的初始化会和c3p0的懒加载机制扯上关系吗?
答案是必须有关系啊。

4.1资源的实例化过程

可以这么理解,要想创建连接,必须先有资源池;
要想有资源池,必须有连接池;
要想有连接池,必须有连接池管理器,但有了连接池管理器还不够,你得用到我的时候我才会去创建;
要想有连接池管理器,必须有数据源,但有了数据源还不行,你得用到我的时候我才会去创建。
所以,当第一次获取连接的时候,有线程负责去实例化连接池和连接池管理器。

  1. 创建连接池管理器
    AbstractPoolBackedDataSource.java
private synchronized C3P0PooledConnectionPoolManager getPoolManager() throws SQLException
{
    if (poolManager == null)
    {
        ConnectionPoolDataSource cpds = assertCpds();
        poolManager = new C3P0PooledConnectionPoolManager(cpds, null, null, this.getNumHelperThreads(), this.getIdentityToken());
        if (logger.isLoggable(MLevel.INFO))
            logger.info("Initializing c3p0 pool... " + this.toString()  /* + "; using pool manager: " + poolManager */);
    }
    return poolManager;     
}

2.创建连接池
C3P0PooledConnectionPoolManager.java

public synchronized C3P0PooledConnectionPool getPool(DbAuth auth)
throws SQLException
{
    C3P0PooledConnectionPool out = (C3P0PooledConnectionPool) authsToPools.get(auth);
    if (out == null)
    {
        out = createPooledConnectionPool(auth);
        authsToPools.put( auth, out );
    }
    return out;
}

private C3P0PooledConnectionPool createPooledConnectionPool(DbAuth auth) throws SQLException
{
    String userName = auth.getUser();
    String automaticTestTable = getAutomaticTestTable( userName );
    String realTestQuery;

    if (automaticTestTable != null)
    {
        realTestQuery = initializeAutomaticTestTable( automaticTestTable, auth );
        if (this.getPreferredTestQuery( userName ) != null)
        {
            if ( logger.isLoggable( MLevel.WARNING ) )
            {
                logger.logp(MLevel.WARNING, 
                                C3P0PooledConnectionPoolManager.class.getName(),
                                "createPooledConnectionPool",
                                "[c3p0] Both automaticTestTable and preferredTestQuery have been set! " +
                                "Using automaticTestTable, and ignoring preferredTestQuery. Real test query is ''{0}''.",
                                realTestQuery
                );
            }
        }
    }
    else
    {
    // when there is an automaticTestTable to be constructed, we
    // have little choice but to grab a Connection on initialization
    // to ensure that the table exists before the pool tries to
    // test Connections. in c3p0-0.9.1-pre10, i added the check below
    // to grab and destroy a cxn even when we don't
    // need one, to ensure that db access params are correct before
    // we start up a pool. a user who frequently creates and destroys
    // PooledDataSources complained about the extra initialization
    // time. the main use of this test was to prevent superfluous
    // bad pools from being intialized when JMX users type bad
    // authentification information into a query method. This is
    // now prevented in AbstractPoolBackedDataSource. Still, it is
    // easy for clients to start pools uselessly by asking for
    // Connections with bad authentification information. We adopt
    // the compromise position of "trusting" the DataSource's default
    // authentification info (as defined by defaultAuth), but ensuring
    // that authentification succeeds via the check below when non-default
    // authentification info is provided.

    if (! defaultAuth.equals( auth ))
    ensureFirstConnectionAcquisition( auth );

        realTestQuery = this.getPreferredTestQuery( userName );
    }

    C3P0PooledConnectionPool out =  new C3P0PooledConnectionPool( cpds,
                    auth,
                    this.getMinPoolSize( userName ),
                    this.getMaxPoolSize( userName ),
                    this.getInitialPoolSize( userName ),
                    this.getAcquireIncrement( userName ),
                    this.getAcquireRetryAttempts( userName ),
                    this.getAcquireRetryDelay( userName ),
                    this.getBreakAfterAcquireFailure( userName ),
                    this.getCheckoutTimeout( userName ),
                    this.getIdleConnectionTestPeriod( userName ),
                    this.getMaxIdleTime( userName ),
                    this.getMaxIdleTimeExcessConnections( userName ),
                    this.getMaxConnectionAge( userName ),
                    this.getPropertyCycle( userName ),
                    this.getUnreturnedConnectionTimeout( userName ),
                    this.getDebugUnreturnedConnectionStackTraces( userName ),
                    this.getTestConnectionOnCheckout( userName ),
                    this.getTestConnectionOnCheckin( userName ),
                    this.getMaxStatements( userName ),
                    this.getMaxStatementsPerConnection( userName ),
                    this.getConnectionTester( userName ),
                    this.getConnectionCustomizer( userName ),
                    realTestQuery,
                    rpfact,
                    taskRunner,
                    parentDataSourceIdentityToken );
    return out;
}

3.创建资源池
C3P0PooledConnectionPool.java

C3P0PooledConnectionPool( final ConnectionPoolDataSource cpds,
                final DbAuth auth,
                int min, 
                int max, 
                int start,
                int inc,
                int acq_retry_attempts,
                int acq_retry_delay,
                boolean break_after_acq_failure,
                int checkoutTimeout, //milliseconds
                int idleConnectionTestPeriod, //seconds
                int maxIdleTime, //seconds
                int maxIdleTimeExcessConnections, //seconds
                int maxConnectionAge, //seconds
                int propertyCycle, //seconds
                int unreturnedConnectionTimeout, //seconds
                boolean debugUnreturnedConnectionStackTraces,
                final boolean testConnectionOnCheckout,
                final boolean testConnectionOnCheckin,
                int maxStatements,
                int maxStatementsPerConnection,
                final ConnectionTester connectionTester,
                final ConnectionCustomizer connectionCustomizer,
                final String testQuery,
                final ResourcePoolFactory fact,
                ThreadPoolAsynchronousRunner taskRunner,
                final String parentDataSourceIdentityToken) throws SQLException
                {
    try
    {
        if (maxStatements > 0 && maxStatementsPerConnection > 0)
            this.scache = new DoubleMaxStatementCache( taskRunner, maxStatements, maxStatementsPerConnection );
        else if (maxStatementsPerConnection > 0)
            this.scache = new PerConnectionMaxOnlyStatementCache( taskRunner, maxStatementsPerConnection );
        else if (maxStatements > 0)
            this.scache = new GlobalMaxOnlyStatementCache( taskRunner, maxStatements );
        else
            this.scache = null;

        this.connectionTester = connectionTester;

        this.checkoutTimeout = checkoutTimeout;

        this.sharedTaskRunner = taskRunner;
        ResourcePool.Manager manager = new PooledConnectionResourcePoolManager();

        synchronized (fact)
        {
            fact.setMin( min );
            fact.setMax( max );
            fact.setStart( start );
            fact.setIncrement( inc );
            fact.setIdleResourceTestPeriod( idleConnectionTestPeriod * 1000);
            fact.setResourceMaxIdleTime( maxIdleTime * 1000 );
            fact.setExcessResourceMaxIdleTime( maxIdleTimeExcessConnections * 1000 );
            fact.setResourceMaxAge( maxConnectionAge * 1000 );
            fact.setExpirationEnforcementDelay( propertyCycle * 1000 );
            fact.setDestroyOverdueResourceTime( unreturnedConnectionTimeout * 1000 );
            fact.setDebugStoreCheckoutStackTrace( debugUnreturnedConnectionStackTraces );
            fact.setAcquisitionRetryAttempts( acq_retry_attempts );
            fact.setAcquisitionRetryDelay( acq_retry_delay );
            fact.setBreakOnAcquisitionFailure( break_after_acq_failure );
            rp = fact.createPool( manager );
        }
    }
    catch (ResourcePoolException e)
    { throw SqlUtils.toSQLException(e); }
                }

BasicResourcePoolFactory.java

public synchronized ResourcePool createPool(ResourcePool.Manager mgr)
throws ResourcePoolException
{
if (liveChildren == null)
    createThreadResources();
//System.err.println("Created liveChildren: " + liveChildren);
ResourcePool child = new BasicResourcePool( mgr, 
                        start, 
                        min, 
                        max, 
                        inc, 
                        retry_attempts, 
                        retry_delay, 
                        idle_resource_test_period,
                        max_age, 
                        max_idle_time,
                        excess_max_idle_time,
                        destroy_overdue_resc_time,
                        expiration_enforcement_delay,
                        break_on_acquisition_failure,
                        debug_store_checkout_stacktrace,
                        taskRunner,
                        asyncEventQueue,
                        timer,
                        this );
liveChildren.add( child );
return child;
}
  1. 创建资源
    WrapperConnectionPoolDataSource.java
public PooledConnection getPooledConnection()
throws SQLException
{ return this.getPooledConnection( (ConnectionCustomizer) null, null ); }

// getNestedDataSource() is sync'ed, which is enough. Unsync'ed this method,
// because when sync'ed a hang in retrieving one connection blocks all
//
protected PooledConnection getPooledConnection( ConnectionCustomizer cc, String pdsIdt )
throws SQLException
{ 
DataSource nds = getNestedDataSource();
if (nds == null)
    throw new SQLException( "No standard DataSource has been set beneath this wrapper! [ nestedDataSource == null ]");
Connection conn = nds.getConnection();
if (conn == null)
    throw new SQLException("An (unpooled) DataSource returned null from its getConnection() method! " +
               "DataSource: " + getNestedDataSource());
if ( this.isUsesTraditionalReflectiveProxies() )
    {
    //return new C3P0PooledConnection( new com.mchange.v2.c3p0.test.CloseReportingConnection( conn ), 
    return new C3P0PooledConnection( conn, 
                     connectionTester,
                     this.isAutoCommitOnClose(), 
                     this.isForceIgnoreUnresolvedTransactions(),
                     cc,
                     pdsIdt); 
    }
else
    {
    return new NewPooledConnection( conn, 
                    connectionTester,
                    this.isAutoCommitOnClose(), 
                    this.isForceIgnoreUnresolvedTransactions(),
                    this.getPreferredTestQuery(),
                    cc,
                    pdsIdt); 
    }
} 

C3P0PooledConnectionPool.Manager.java

            public Object acquireResource() throws Exception
            { 
                PooledConnection out;

                if ( connectionCustomizer == null)
                {
                    out = (auth.equals( C3P0ImplUtils.NULL_AUTH ) ?
                           cpds.getPooledConnection() :
                           cpds.getPooledConnection( auth.getUser(), 
                                                     auth.getPassword() ) );
                }
                else
                {
                    try
                    { 
                        WrapperConnectionPoolDataSourceBase wcpds = (WrapperConnectionPoolDataSourceBase) cpds;

                        out = (auth.equals( C3P0ImplUtils.NULL_AUTH ) ?
                               wcpds.getPooledConnection( connectionCustomizer, parentDataSourceIdentityToken ) :
                               wcpds.getPooledConnection( auth.getUser(), 
                                                          auth.getPassword(),
                                                          connectionCustomizer, parentDataSourceIdentityToken ) );
                    }
                    catch (ClassCastException e)
                    {
                        throw SqlUtils.toSQLException("Cannot use a ConnectionCustomizer with a non-c3p0 ConnectionPoolDataSource." +
                                        " ConnectionPoolDataSource: " + cpds.getClass().getName(), e);
                    }
                }

                //connectionCounter.increment(); 
                //totalOpenedCounter.increment();

                try
                {
                    if (scache != null)
                    {
                        if (c3p0PooledConnections)
                            ((AbstractC3P0PooledConnection) out).initStatementCache(scache);
                        else
                        {
                            // System.err.print("Warning! StatementPooling not ");
                            // System.err.print("implemented for external (non-c3p0) ");
                            // System.err.println("ConnectionPoolDataSources.");

                            logger.warning("StatementPooling not " +
                                            "implemented for external (non-c3p0) " +
                            "ConnectionPoolDataSources.");
                        }
                    }
                    
                    // log and clear any SQLWarnings present upon acquisition
                    Connection con = null;
                    try
                    {
                        con = out.getConnection();
                        SQLWarnings.logAndClearWarnings( con );
                    }
                    finally
                    {
                        //invalidate the proxy Connection
                        ConnectionUtils.attemptClose( con );
                    }
                    
                    out.addConnectionEventListener( cl );
                    return out;
                }
                catch (Exception e)
                {
                    if (logger.isLoggable( MLevel.WARNING ))
                        logger.warning("A PooledConnection was acquired, but an Exception occurred while preparing it for use. " +
                        "Attempting to destroy.");
                    try { destroyResource( out ); }
                    catch (Exception e2)
                    {
                        if (logger.isLoggable( MLevel.WARNING ))
                            logger.log( MLevel.WARNING, 
                                            "An Exception occurred while trying to close partially acquired PooledConnection.",
                                            e2 );
                    }

                    throw e;
                }
                finally
                {
                    if (logger.isLoggable( MLevel.FINEST ))
                        logger.finest(this + ".acquireResource() returning. " );
                    //"Currently open Connections: " + connectionCounter.getValue() +
                    //"; Failed close count: " + failedCloseCounter.getValue() +
                    //"; Total processed by this pool: " + totalOpenedCounter.getValue());
                }
            }

BasicResourcePool.java

private void doAcquire() throws Exception
{
    assert !Thread.holdsLock( this );

    Object resc = mgr.acquireResource(); //note we acquire the resource while we DO NOT hold the pool's lock!

    boolean destroy = false;
    int msz;

    synchronized(this) //assimilate resc if we do need it
    {
//          ++total_acquired;

//          if (logger.isLoggable( MLevel.FINER))
//          logger.log(MLevel.FINER, "acquired new resource, total_acquired: " + total_acquired);

        msz = managed.size();
        if (msz < target_pool_size)
            assimilateResource(resc); 
        else
            destroy = true;
    }

    if (destroy)
    {
        mgr.destroyResource( resc ); //destroy resc if superfluous, without holding the pool's lock
        if (logger.isLoggable( MLevel.FINER))
            logger.log(MLevel.FINER, "destroying overacquired resource: " + resc);
    }
}

5 最后演示下资源的释放过程

资源释放过程

1.开始释放

        Connection connection = cpds.getConnection();
        connection.close();

2.NewProxyConnectionNewPooledConnection解除绑定,并清楚自己

public synchronized void close() throws SQLException {
    try {
        if (!this.isDetached()) {
            NewPooledConnection npc = this.parentPooledConnection;
            this.detach();
            npc.markClosedProxyConnection(this, this.txn_known_resolved);
            this.inner = null;
        } else if (logger.isLoggable(MLevel.FINE)) {
            logger.log(MLevel.FINE, this + ": close() called more than once.");
        }
    } catch (NullPointerException var2) {
        if (!this.isDetached()) {
            throw var2;
        }

        if (logger.isLoggable(MLevel.FINE)) {
            logger.log(MLevel.FINE, this + ": close() called more than once.");
        }
    } catch (Exception var3) {
        if (!this.isDetached()) {
            throw this.parentPooledConnection.handleThrowable(var3);
        }

        throw SqlUtils.toSQLException(var3);
    }

}

3.NewPooledConnection清除缓存,并尝试关闭

void markClosedProxyConnection( NewProxyConnection npc, boolean txn_known_resolved ) 
{
    SQLException trouble = null;
    try
    {
        synchronized( this )
        {
            try
            {
                if (npc != exposedProxy)
                    throw new InternalError("C3P0 Error: An exposed proxy asked a PooledConnection that was not its parents to clean up its resources!");

                List closeExceptions = new LinkedList();
                cleanupResultSets( closeExceptions );
                cleanupUncachedStatements( closeExceptions );
                checkinAllCachedStatements( closeExceptions );
                if ( closeExceptions.size() > 0 )
                {
//                      System.err.println("[c3p0] The following Exceptions occurred while trying to clean up a Connection's stranded resources:");
                    if ( logger.isLoggable( MLevel.INFO ) )
                        logger.info("[c3p0] The following Exceptions occurred while trying to clean up a Connection's stranded resources:");
                    for ( Iterator ii = closeExceptions.iterator(); ii.hasNext(); )
                    {
                        Throwable t = (Throwable) ii.next();
//                          System.err.print("[c3p0 -- conection resource close Exception]: ");
//                          t.printStackTrace();
                        if ( logger.isLoggable( MLevel.INFO ) )
                            logger.log( MLevel.INFO, "[c3p0 -- conection resource close Exception]", t );
                    }
                }
                reset( txn_known_resolved );
            }
            catch (SQLException e) //Connection failed to reset!
            {
                //e.printStackTrace();
                if (Debug.DEBUG && logger.isLoggable( MLevel.FINE ))
                    logger.log(MLevel.FINE, "An exception occurred while reseting a closed Connection. Invalidating Connection.", e);

                updateConnectionStatus( ConnectionTester.CONNECTION_IS_INVALID );
            }
        }
    }
    finally
    {
        if (trouble != null)
            fireConnectionErrorOccurred( trouble ); //should not be invoked from a sync'ed block
        else
        {
            exposedProxy = null; //volatile
            fireConnectionClosed(); //should not be invoked from a sync'ed block
        }
    }
}

private void fireConnectionClosed()
{
    assert (! Thread.holdsLock(this));
    ces.fireConnectionClosed(); 
}

4.ConnectionEventSupport尝试关闭

public void fireConnectionClosed()
{
Set mlCopy;

synchronized (this)
    { mlCopy = (Set) mlisteners.clone(); }

ConnectionEvent evt = new ConnectionEvent(source);
for (Iterator i = mlCopy.iterator(); i.hasNext();)
    {
    ConnectionEventListener cl = (ConnectionEventListener) i.next();
    cl.connectionClosed(evt);
    }
}

5.C3P0PooledConnectionPool回收资源

    public void connectionClosed(final ConnectionEvent evt)
    { 
        //System.err.println("Checking in: " + evt.getSource());

        if (ASYNCHRONOUS_CONNECTION_EVENT_LISTENER) 
        {
            Runnable r = new Runnable()
            {
                public void run()
                { doCheckinResource( evt ); }
            };
            sharedTaskRunner.postRunnable( r );
        }
        else
            doCheckinResource( evt );
    }

    private void doCheckinResource(ConnectionEvent evt)
    {
        try
        { rp.checkinResource( evt.getSource() ); }
        catch (Exception e)
        { 
            //e.printStackTrace(); 
            logger.log( MLevel.WARNING, 
                            "An Exception occurred while trying to check a PooledConection into a ResourcePool.",
                            e );
        }
    }

6.BasicResourcePool回收资源

public synchronized void checkinResource( Object resc ) 
throws ResourcePoolException
{
    try
    {
        //we permit straggling resources to be checked in 
        //without exception even if we are broken
        if (managed.keySet().contains(resc))
            doCheckinManaged( resc );
        else if (excluded.contains(resc))
            doCheckinExcluded( resc );
        else if ( isFormerResource(resc) )
        {
            if ( logger.isLoggable( MLevel.FINER ) )
                logger.finer("Resource " + resc + " checked-in after having been checked-in already, or checked-in after " +
                " having being destroyed for being checked-out too long.");
        }
        else
            throw new ResourcePoolException("ResourcePool" + (broken ? " [BROKEN!]" : "") + ": Tried to check-in a foreign resource!");
        if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) trace();
    }
    catch ( ResourceClosedException e ) // one of our async threads died
    {
//          System.err.println(this + 
//          " - checkinResource( ... ) -- even broken pools should allow checkins without exception. probable resource pool bug.");
//          e.printStackTrace();

        if ( logger.isLoggable( MLevel.SEVERE ) )
            logger.log( MLevel.SEVERE, 
                            this + " - checkinResource( ... ) -- even broken pools should allow checkins without exception. probable resource pool bug.", 
                            e);

        this.unexpectedBreak();
        throw e;
    }
}

你可能感兴趣的:(c3p0 探索计划)