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

本文详细解析了Apache Commons Pool框架中的GenericObjectPool类,探讨其作为对象池核心组件的角色,以及与对象工厂、配置和接口的关系。介绍了GenericObjectPool的构造方法、内部属性和关键接口的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

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.youkuaiyun.com/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.youkuaiyun.com/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.youkuaiyun.com/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.youkuaiyun.com/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();
}
<think>好的,我现在需要解决用户在使用JedisPoolConfig配置时遇到的错误:“Cannot access org.apache.commons.pool.impl.GenericObjectPool.Config”。用户提到在调用setMaxActive方法时出现了这个问题。首先,我应该回忆一下Jedis和Apache Commons Pool的版本兼容性问题。 我记得,旧版本的Jedis可能使用的是Apache Commons Pool 1.x的库,而新版本可能升级到了Commons Pool 2.x。在Commons Pool 1.x中,配置类如GenericObjectPool.Config确实位于org.apache.commons.pool.impl包下。而升级到2.x后,包结构发生了变化,现在配置类可能移动到了org.apache.commons.pool2.impl,并且方法名称也有所调整,例如setMaxActive可能改为了setMaxTotal。 所以,用户的错误可能是因为他们正在使用Commons Pool 2.x的依赖,但代码中引用了旧版本1.x的类或方法。例如,用户可能在调用JedisPoolConfig的setMaxActive方法,而该方法在Commons Pool 2.x中不存在,取而代之的是setMaxTotal。这时候,编译器就会报错,提示无法访问旧包中的类。 接下来,我需要确认用户的项目依赖是否正确。如果他们确实在使用Commons Pool 2.x,那么应该修改代码中的方法调用和导入的包路径。此外,检查Maven或Gradle构建文件中的依赖版本,确保Jedis和Commons Pool的版本兼容。例如,Jedis 3.x以上版本通常需要Commons Pool2,而旧版本可能仍在使用Commons Pool1。 另外,用户可能在代码中显式引用了GenericObjectPool.Config,比如在配置池参数时,这时候需要将导入的包从org.apache.commons.pool.impl改为org.apache.commons.pool2.impl,并且使用新的配置类,如GenericObjectPoolConfig。同时,相关的方法名也需要调整,比如setMaxActive改为setMaxTotal,setMaxWait改为setMaxWaitMillis等。 可能还需要检查Spring Data Redis的配置是否正确,因为用户提到了RedisConnectionFactory的构造方法。如果Spring Data Redis版本较新,它可能默认使用Lettuce而不是Jedis,这时候需要排除Lettuce依赖并显式引入Jedis。这也会影响连接池的配置方式。 最后,建议用户更新依赖版本,并参考官方文档中的迁移指南,确保所有配置项都适配新版本的库。同时,提供一个示例配置,展示如何正确使用JedisPoolConfig和GenericObjectPoolConfig来设置连接池参数,避免包路径和方法名的错误。</think>### 解决方案:JedisPoolConfig配置报错`Cannot access org.apache.commons.pool.impl.GenericObjectPool.Config` 该错误通常是由于Jedis依赖的Apache Commons Pool版本不兼容或配置方法过时导致。以下是具体原因和解决方法: --- #### **1. 依赖版本冲突** Jedis的早期版本(如2.x)依赖`commons-pool 1.x`,而新版本(3.x及以上)改用`commons-pool2 2.x`。若项目中混用新旧版本依赖,会导致类路径不匹配。 **解决方法**: 1. **检查依赖版本**: - 若使用Maven,检查`pom.xml`中的依赖声明: ```xml <!-- 使用Jedis 3.x及以上版本,需依赖commons-pool2 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>4.4.1</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.11.1</version> </dependency> ``` - 移除旧版`commons-pool 1.x`依赖,避免冲突[^2][^4]。 --- #### **2. 配置方法过时** 在`commons-pool2 2.x`中,`GenericObjectPool.Config`已迁移到包`org.apache.commons.pool2.impl`,且配置参数名称有调整: - `setMaxActive` → `setMaxTotal` - `setMaxWait` → `setMaxWaitMillis` **正确配置示例**: ```java import org.apache.commons.pool2.impl.GenericObjectPoolConfig; public JedisPool jedisPool() { GenericObjectPoolConfig<Jedis> poolConfig = new GenericObjectPoolConfig<>(); poolConfig.setMaxTotal(100); // 替代setMaxActive poolConfig.setMaxWaitMillis(3000); // 替代setMaxWait poolConfig.setTestOnBorrow(true); return new JedisPool(poolConfig, "localhost", 6379); } ``` --- #### **3. Spring Data Redis的配置适配** 若通过Spring Boot配置连接池,需确保使用`JedisConnectionFactory`时传递兼容的配置: ```yaml # application.yml spring: redis: host: localhost port: 6379 jedis: pool: max-active: 100 # 对应maxTotal max-wait: 3000ms # 对应maxWaitMillis ``` Spring Boot会自动将配置映射到`commons-pool2`的参数[^3]。 --- #### **4. 排查依赖冲突** 使用Maven/Gradle检查依赖树,确保无旧版`commons-pool`残留: ```bash # Maven mvn dependency:tree | grep commons-pool # Gradle gradle dependencies | grep commons-pool ``` 若发现旧版本,通过`<exclusion>`移除冲突依赖。 --- ### **总结步骤** 1. 升级Jedis到3.x+,并依赖`commons-pool2`。 2. 修改代码中的配置类和方法名(如`setMaxTotal`)。 3. 检查Spring配置的兼容性。 4. 清理旧版本依赖。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值