使用apache commons pool时遇到IllegalStateException:returned object is not part of this pool

最近在做一个项目时,碰到了这个问题。琢磨了好长时间代码,都没有发现问题。原本以为是过滤器和切面的异步操作造成最后调用returnObject时多调用了一次,但是把过滤器中的相关代码去掉之后,发现还是这个问题。

一个多小时,就为了解决这个问题。下面说明解法:

        先上出问题的代码:

      

package com.projecthome.aspect;

import java.lang.reflect.Method;

import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import com.projecthome.bean.User;
import com.projecthome.obejctpool.KeyUserObjectPoolFactory;
import com.projecthome.util.ConvertXmlToObject;

/**
 * 
 * 在这个切面中装填 对象池中的相应的<span>User</span>对象
 * 因为我们在过滤器中,以及后面的方法中,对于同一个<span>email</span>处理的是同一个对象,所以这里完全可以这么做
 * 
 * @author AlstonWilliams
 * @date 2016/03/19
 * @contact pshuyue@gmail.com
 * */
@Aspect
@Component
public class FillUserAspect {
          @Pointcut("execution(* com.projecthome.operation.UserOperation.query*(..))")
           public void declareExpression(){}

           @Before("declareExpression()")
           public void beforeCall(JoinPoint joinPoint) throws Exception{
               String email = joinPoint.getArgs()[0].toString();
               GenericKeyedObjectPool<String, User> userPool = new GenericKeyedObjectPool<String, User>(KeyUserObjectPoolFactory.newInstance());
               User user = userPool.borrowObject(email);
               String url = "http://localhost/UserService_3.0/dao/v3/user/find?useremail=pshuyue@gmail.com";
               HttpGet httpGet = new HttpGet(url);
               user = (User) ConvertXmlToObject.newInstance().convert(user, EntityUtils.toString(new DefaultHttpClient().execute(httpGet).getEntity(), "utf-8"));
               if (null != user) {
                     userPool.returnObject(email, user);}
  }
}



 

其中ConvertXmlToObject这个类是我自己写的一个工具类,用于在约定好的情况下将xml文档直接转换成一个对象,这样调用起来方便。下面是这个类的代码:

package com.projecthome.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * 
 * 将一个xml文件转换成一个对象
 * 
 * 能完成这种转换是基于约定的:
 * 	1.该xml文档中除了根节点只能有一级节点
 * 	2.该一级节点必须对应要转换的Bean中的一个属性
 * 
 * @author AlstonWilliams
 * @date 2016/03/15
 * @contact pshuyue@gmail.com
 * 
 * */
public class ConvertXmlToObject {

	private static ConvertXmlToObject convertXmlToObject;
	
	private ConvertXmlToObject(){}
	
	public static ConvertXmlToObject newInstance(){
		if (convertXmlToObject == null) {
			convertXmlToObject = new ConvertXmlToObject();
		}
		return convertXmlToObject;
	}
	
	/*
	 * @param fullClassName qualified name of class
	 * @param xml string whose format is xml
	 * 
	 */
	public Object convert(String fullClassName,String xml) throws ClassNotFoundException, DocumentException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		Object object = Class.forName(fullName).newInstance();		
		SAXReader saxReader = new SAXReader();
		Document document = DocumentHelper.parseText(xml);
		
		Element root = document.getRootElement();
		Iterator<Element> iterator = root.elementIterator();
		while (iterator.hasNext()) {
			Element element = (Element) iterator.next();
			String elementName = element.getName().toLowerCase();
			String methodName = "set"
					+ new Character(elementName.charAt(0)).toString().toUpperCase()
					+ elementName.substring(1);
			Method method = object.getClass().getMethod(methodName, String.class);
			method.invoke(object, element.getText());
		}
		
		return object;
	}
	
}


问题就出在上面这个工具类上,我们可以看到,这里我们是根据类的完全限定名来加载类,并创建了一个新的对象,然后作为返回值赋值给了FillUserAspect这个切面中的User对象。问题就是出在这里。根据apache commons pool的官方文档中的解释,出现IllegalStateException这个异常是由于
* @throws IllegalStateException if an object is returned to the pool that
*                               was not borrowed from it or if an object is
*                               returned to the pool multiple times

出在 https://commons.apache.org/proper/commons-pool/api-2.0/src-html/org/apache/commons/pool2/impl/GenericKeyedObjectPool.html

可以看到,如果你要returnObject的对象不是你从borrow方法中获取的那个对象,那么就会抛出异常。那么就很明显了,因为"="运算符是将一个对象的引用赋值给另一个对象,所以其实是将ConvertXmlToObject中的convert方法中的object的引用赋值给了FillUserAspect中的user对象了。这时user对象指向的当然就不是以前的对象。我们通过比较赋值前后的user对象的hash值就能清楚的得到这个结论。既然原来的对象了,自然就不能使用returnObject方法来返回了。


于是乎,我修改了ConvertXmlToObject中的convert方法,现在就一切正常了:

package com.projecthome.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * 
 * 将一个xml文件转换成一个对象
 * 
 * 能完成这种转换是基于约定的:
 * 	1.该xml文档中除了根节点只能有一级节点
 * 	2.该一级节点必须对应要转换的Bean中的一个属性
 * 
 * @author AlstonWilliams
 * @date 2016/03/15
 * @contact pshuyue@gmail.com
 * 
 * */
public class ConvertXmlToObject {

	private static ConvertXmlToObject convertXmlToObject;
	
	private ConvertXmlToObject(){}
	
	public static ConvertXmlToObject newInstance(){
		if (convertXmlToObject == null) {
			convertXmlToObject = new ConvertXmlToObject();
		}
		return convertXmlToObject;
	}
	
	/*
	 * @param object object you want to convert
	 * @param xml string whose format is xml
	 * 
	 */
	public Object convert(Object object,String xml) throws ClassNotFoundException, DocumentException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		
		SAXReader saxReader = new SAXReader();
		Document document = DocumentHelper.parseText(xml);
		
		Element root = document.getRootElement();
		Iterator<Element> iterator = root.elementIterator();
		while (iterator.hasNext()) {
			Element element = (Element) iterator.next();
			String elementName = element.getName().toLowerCase();
			String methodName = "set"
					+ new Character(elementName.charAt(0)).toString().toUpperCase()
					+ elementName.substring(1);
			Method method = object.getClass().getMethod(methodName, String.class);
			method.invoke(object, element.getText());
		}
		
		return object;
	}
	
}

以前看到这个知识点,一直也没有注意,直到今天调试时,注意到赋值前后的user对象的哈希码不同,才明白过来。今后也应当注意才是。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Apache Commons Pool 是一个用于对象池化技术的开源 Java 库。它提供了一组可重用对象的通用接口和基于通用接口的实现,可以用于构建高性能、可扩展和线程安全的对象池。 以下是一个简单的使用 Apache Commons Pool 的例子: 1. 添加 Maven 依赖: ```xml <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>2.9.0</version> </dependency> ``` 2. 创建一个可重用对象的类: ```java public class Connection { private String url; private String username; private String password; // getters and setters } ``` 3. 创建一个工厂类来创建和销毁对象: ```java public class ConnectionFactory extends BasePoolableObjectFactory<Connection> { private final String url; private final String username; private final String password; public ConnectionFactory(String url, String username, String password) { this.url = url; this.username = username; this.password = password; } @Override public Connection makeObject() throws Exception { return new Connection(url, username, password); } @Override public void destroyObject(Connection connection) throws Exception { connection.close(); } } ``` 4. 创建一个对象池: ```java public class ConnectionPool { private GenericObjectPool<Connection> pool; public ConnectionPool(String url, String username, String password) { ConnectionFactory factory = new ConnectionFactory(url, username, password); GenericObjectPoolConfig<Connection> config = new GenericObjectPoolConfig<>(); config.setMaxTotal(10); pool = new GenericObjectPool<>(factory, config); } public Connection getConnection() throws Exception { return pool.borrowObject(); } public void releaseConnection(Connection connection) { pool.returnObject(connection); } } ``` 5. 使用对象池: ```java ConnectionPool pool = new ConnectionPool("jdbc:mysql://localhost:3306/test", "root", "password"); Connection connection = pool.getConnection(); // use connection pool.releaseConnection(connection); ``` 这个例子中,我们创建了一个 Connection 对象,使用了 ConnectionFactory 类来创建和销毁它们。我们还创建了一个 ConnectionPool 类来管理对象池,并使用它来获取和释放 Connection 对象。最后,我们展示了如何使用 Connection 对象来执行数据库操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值