您的当前位置:首页正文

对象池(连接池):commons-pool2源码解析:GenericObjectPool的继承结构、构造方法

2024-11-08 来源:个人技术集锦

概述

GenericObjectPool是apache commons pool(源码分析基于commons pool2)框架中的一个非常重要的类。解析GenericObjectPool就有必要先了解一下apache commons pool。

apache commons pool(2)

apache commons pool框架是一个对象池框架,提供了非常通用的对象池的api以及实现。业务开发中非常常用的连接池,无论是数据库连接池、redis连接池、http连接池等,都可以基于commons pool来实现。例如:数据库连接池DBCP、redis连接池Jedis/lettuce。

对象池里几个比较重点的组件

对象池里几个核心的接口和类

我们的源码解析的切入点是GenericObjectPool,那么就以GenericObjectPool为触发点,看一下他的继承结构。

  • GenericObjectPool继承了BaseGenericObjectPool类
  • GenericObjectPool实现了ObjectPool接口(UsageTracking、GenericObjectPoolMXBean接口我们在其他文章中解析)
  • GenericObjectPool的构造函数依赖了PooledObjectFactory接口
ObjectPool接口

ObjectPool接口定义了对象池的通用行为。

  • borrowObject:从对象池中获取对象
  • returnObject:把对象归还给对象池
  • invalidateObject:清理作废一个对象(释放池中一个资源)
  • addObject:往对象池中添加一个对象
  • getNumIdle:获取空闲对象的数量
  • getNumActive:获取活跃对象的数量
  • clear:目的是为了清理所有空闲对象
  • close:关闭连接池,释放所有池中的资源。

也就是说任何一个ObjectPool接口的具体的实现类都必须要实现以上方法。

BaseGenericObjectPool类

先看下BaseGenericObjectPool类的声明

public abstract class BaseGenericObjectPool<T> {
    // 略去了类中代码
}

BaseGenericObjectPool是一个抽象类,虽然从名称上看,貌似是一个基础的对象池实现,但并不是,因为他没有继承ObjectPool接口。再看下和他相关的继承结构。

  • 他没有继承任何类,也没有实现任何接口。
  • 有两个子类,分别是
    • GenericObjectPool
    • GenericKeyedObjectPool

再来看下他内部的属性和方法定义

Base class that provides common functionality for {@link GenericObjectPool} and {@link GenericKeyedObjectPool}. The primary reason this class exists is reduce code duplication between the two pool implementations.

意思就是这个类给GenericObjectPool和GenericKeyedObjectPool提供一个通用的功能,这个类存在的主要原因就是为了消除重复代码。

PooledObjectFactory接口

PooledObjectFactory接口定义了对象工厂的通用行为

  • makeObject:创建对象
  • destroyObject:销毁对象(回收资源)
  • validateObject:对象的校验(重点是否可用)
  • activateObject:激活对象(执行一些初始化动作,之后就意味着可以被使用了)
    • jedis连接池的PooledObjectFactory实现是JedisFactory,对于这个激活方法的实现是:做了一个redis的select连库请求。
    • dbcp连接池的PooledObjectFactory的实现是PoolableConnectionFactory,对于这个激活方法的实现是:设置数据库连接的autocommit、readonly等属性。
  • passivateObject:是activateObject的操作,封存对象
    • jedis连接池的PooledObjectFactory实现是JedisFactory,对于这个方法的实现逻辑为空。
    • dbcp连接池的PooledObjectFactory的实现是PoolableConnectionFactory,对于这个方法的实现逻辑针对数据库连接做了事务的提交和回滚操作。

GenericObjectPool的构造方法

以下是摘取了GenericObjectPool中和构造方法相关的部分代码片段

// 空闲对象列表
private final LinkedBlockingDeque<PooledObject<T>> idleObjects;
// JMX 需要的特定属性
private static final String ONAME_BASE =
    "org.apache.commons.pool2:type=GenericObjectPool,name=";
/*
 这个一个附加的配置,用来针对遗弃对象的管理和追踪
 关于abandonedConfig可以参见另一篇解析:https:///weixin_42340670/article/details/107136994
*/ 
private volatile AbandonedConfig abandonedConfig = null;


/**
 * 传入:对象工厂
 * factory 对象工厂
 */
public GenericObjectPool(PooledObjectFactory<T> factory) {
    // 使用默认的对象池配置,调用下一个重载的构造方法
    this(factory, new GenericObjectPoolConfig()); 
}

/**
 * 传入:对象工厂、对象池配置
 * factory 对象工厂
 * config  对象池配置
 */
public GenericObjectPool(PooledObjectFactory<T> factory,
        GenericObjectPoolConfig config) {

    // 调用父类BaseGenericObjectPool的构造方法,下面会单独解析
    super(config, ONAME_BASE, config.getJmxNamePrefix());

    // 这个校验意思很明确,要想创建一个对象池,必须指定一个对象工厂(否则对象无从产生)。
    if (factory == null) { 
        jmxUnregister(); // 清理jmx
        throw new IllegalArgumentException("factory may not be null");
    }
    this.factory = factory; // 传入的对象工厂赋值给自己内部的实例属性factory

    /*
     idleObjects用来存储空闲对象。
     idleObjects是一个无界的(链表结构的)、阻塞的双端队列.
     config.getFairness()是一个布尔类型返回值,构造LinkedBlockingDeque的时候,用来指定是否维持FIFO的公平性。也就是说请求方来获取对象资源时,是否先到先得(阻塞排队场景下)。
    */
    idleObjects = new LinkedBlockingDeque<PooledObject<T>>(config.getFairness());

    /*
     把传入的对象池配置中的属性都摘取出来,赋值给自己内部的实例属性(定义在父类BaseGenericObjectPool中)
     也就是说GenericObjectPool对象内部并不持有GenericObjectPoolConfig类型的属性,
     他自己内部定义了和GenericObjectPoolConfig类型属性对等的属性。
     配置的值都是从GenericObjectPoolConfig对象中拷贝到GenericObjectPool对象中。
    */ 
    setConfig(config); 

    // 启动回收器(用来定期轮询检测、回收空闲对象)
    startEvictor(getTimeBetweenEvictionRunsMillis());
}

/**
 * 传入:对象工厂、对象池配置、遗弃对象的管理配置
 * factory 对象工厂
 * config  对象池配置
 * abandonedConfig 遗弃对象的管理配置
 */
public GenericObjectPool(PooledObjectFactory<T> factory,
            GenericObjectPoolConfig config, AbandonedConfig abandonedConfig) {
    this(factory, config); // 调用上一个构造方法
    /*
     再单独设置一下abandonedConfig属性。
     关于abandonedConfig可以参见另一篇解析:https:///weixin_42340670/article/details/107136994
    */ 
    setAbandonedConfig(abandonedConfig); 
}

BaseGenericObjectPool的构造方法

在GenericObjectPool的构造方法的开始,调用了

super(config, ONAME_BASE, config.getJmxNamePrefix());

也就是调用了父类BaseGenericObjectPool的构造方法,所以我们接下来再看下BaseGenericObjectPool的该方法的内部逻辑。
以下代码片段,开始的部分列出了一些和构造方法内部逻辑比较相关的属性和方法。

/*
 * 用来标识调用方是否按照先到先得的方式排队等待对象池资源
 * 如果为true,指的就是先到先得
 * 如果为false,不保证先到先得
 * 这个属性也是jmx监控中需要关注的属性
*/
private final boolean fairness;

// 这个方法的意义下面会讲
public final boolean getFairness() {
    return fairness;
}

/*
 * 对象池创建时的堆栈信息
*/
private final String creationStackTrace;

// 这个方法的意义下面会讲
public final String getCreationStackTrace() {
    return creationStackTrace;
}

/*
* Class loader for evictor thread to use since, in a JavaEE or similar
* environment, the context class loader for the evictor thread may not have
* visibility of the correct factory. See POOL-161. Uses a weak reference to
* avoid potential memory leaks if the Pool is discarded rather than closed.

* 用于回收器线程的类加载器。
* 在JavaEE或类似的环境中,回收器上下文类加载器可能对于对象工厂没有可见性
*   多个回收器共用一个Timer,Timer的加载器取决于最先实例化的对象池的加载器。
* 如果池被丢弃而不是关闭,使用弱引用可以避免潜在的内存泄漏。
* 更详细的解释可以参考:https://issues.apache.org/jira/browse/POOL-161
*/
private final WeakReference<ClassLoader> factoryClassLoader;

public BaseGenericObjectPool(BaseObjectPoolConfig config,
        String jmxNameBase, String jmxNamePrefix) {
    
    // 如果配置中设置了要开启jmx
    if (config.getJmxEnabled()) {
        // 把对象池对象注册到jmx中,用来监控对象池的基本信息和运行情况
        this.oname = jmxRegister(config, jmxNameBase, jmxNamePrefix);
    } else {
        this.oname = null;
    }

    // Populate the creation stack trace
    // 生成当前的线程堆栈信息,赋值给creationStackTrace,这个creationStackTrace是jmx监控中需要关注的一个属性。
    this.creationStackTrace = getStackTrace(new Exception());

    // save the current TCCL (if any) to be used later by the evictor Thread
    // 获取(TCCL)线程上下文类加载器,目的是为了在回收线程中使用。
    // 关于类加载器可以参考另外一篇解析:https:///weixin_42340670/article/details/105755422
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    if (cl == null) { // 如果为空,说明当前线程上下文的加载器是根加载器
        factoryClassLoader = null;
    } else { // 如果不为空,把类加载器cl用弱引用对象(只要发生gc,就可以被回收)包装起来,赋值给factoryClassLoader
        factoryClassLoader = new WeakReference<ClassLoader>(cl);
    }

    /* 
     从配置信息中获取fairness的设置,复制给自己的实例变量fairness。
     BaseGenericObjectPool的getFairness方法会返回这个fairness的值
     这个fairness的存在,就是为了让getFairness能够返回,getFairness的存在是为了实现Generic[Keyed]ObjectPoolMXBean接口中定义的getFairness方法
     但是BaseGenericObjectPool并没有实现任何接口,为什么要实现GenericObjectPoolMXBean接口的方法呢?
     因为他的子类GenericObjectPool、GenericKeyedObjectPool的上层MXbean接口都定义了getFairness方法.(jmx监控信息需要关注fairness属性)
     抽象出BaseGenericObjectPool,定义共同的实现,减少代码重复。
    */
    fairness = config.getFairness();
}
显示全文