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

概述

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。

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

  1. 对象池:这个是核心,他承担的对象的管理工作。借用、归还、检测、清理等等,都是他的职责。
  2. 对象工厂:绝对不可或缺,没有了他,对象就无从产生。对象池最初是空的,里面的对象都是在初始化或者借用的时候通过对象工厂来创建的。对象无用了需要销毁,也是通过工厂销毁的。
  3. 配置:不同的应用场景,对于对象池的需求是不一样的。对象池初始要多大,最大限制多少?满了怎么办?空闲对象怎么管理?等等都需要通过配置来定义。

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

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

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

关于UsageTracking接口的用途可以参见另一篇关于abandonedConfig的解析:https://blog.csdn.net/weixin_42340670/article/details/107136994

ObjectPool接口

ObjectPool接口定义了对象池的通用行为。
ObjectPool接口定义
如上图所示:

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

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

BaseGenericObjectPool类

先看下BaseGenericObjectPool类的声明

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

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

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

再来看下他内部的属性和方法定义
BaseGenericObjectPool内部属性方法定义
如上图所示,BaseGenericObjectPool内部定义了很多属性,并提供了getter/setter方法,其中绝大部分都和对象池的配置相关。这个类存在的意义,通过源码中的注释可以很清楚的了解到。

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接口定义了对象工厂的通用行为
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://blog.csdn.net/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://blog.csdn.net/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://blog.csdn.net/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();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值