GenericObjectPool是apache commons pool(源码分析基于commons pool2)框架中的一个非常重要的类。解析GenericObjectPool就有必要先了解一下apache commons pool。
apache commons pool框架是一个对象池框架,提供了非常通用的对象池的api以及实现。业务开发中非常常用的连接池,无论是数据库连接池、redis连接池、http连接池等,都可以基于commons pool来实现。例如:数据库连接池DBCP、redis连接池Jedis/lettuce。
我们的源码解析的切入点是GenericObjectPool,那么就以GenericObjectPool为触发点,看一下他的继承结构。
ObjectPool接口定义了对象池的通用行为。
也就是说任何一个ObjectPool接口的具体的实现类都必须要实现以上方法。
先看下BaseGenericObjectPool类的声明
public abstract class BaseGenericObjectPool<T> {
// 略去了类中代码
}
BaseGenericObjectPool是一个抽象类,虽然从名称上看,貌似是一个基础的对象池实现,但并不是,因为他没有继承ObjectPool接口。再看下和他相关的继承结构。
再来看下他内部的属性和方法定义
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接口定义了对象工厂的通用行为
以下是摘取了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);
}
在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();
}