在使用Proxy.newProxyInstance创建动态代理时,有时会导致类型转换错误。
package cn.itcast.demo;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Properties;
import javax.sql.DataSource;
import com.mysql.jdbc.Connection;
public class JdbcPool implements DataSource {
private static LinkedList<Connection> list = new LinkedList<Connection>();
static{
try{
InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("driver.properties");
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
Class.forName(driver);
for(int i=0;i<10;i++){
Connection conn = (Connection) DriverManager.getConnection(url, username, password);
list.add(conn);
}
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/*
1.写一个子类,覆盖close方法
2、写一个connection的包装类,增强close方法
3、用动态代理,返回一个代理对象出去,拦截close方法的调用,对close进行增强
*/
public Connection getConnection() throws SQLException {
if(list.size()>0){
final Connection conn = list.removeFirst();
Object obj;
obj = Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(!method.getName().equals("close")){
return method.invoke(conn, args);
}else{
list.add(conn);
return null;
}
}
});
return (Connection)obj;
}else{
throw new RuntimeException();
}
}
...
}
此处将会导致类型转换异常:
java.lang.ClassCastException: $Proxy4 cannot be cast to com.mysql.jdbc.Connection
at cn.itcast.utils.JdbcDyPools.getConnection(JdbcDyPools.java:66)
要想分析其原因,还需要对Proxy.newProxyInstance方法进行了解,其原型为
java.lang.reflect.
Proxy.newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws
IllegalArgumentException
它创建的代理实现了interfaces接口,由于conn.getClass().getInterfaces()获取到的接口数组是
com.mysql.jdbc.Connection继承的接口,所以得到的结果是java.sql.Connection,但是这里需要使用com.mysql.jdbc.Connection;所以导致转换错误
两种解决方法:
1、将代码中的Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), conn.getClass().getInterfaces(), hander);改写为:
Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), new Class[]{com.mysql.jdbc.Connection.class}, hander);
2、创建一个接口继承com.mysql.jdbc.Connection
interface MyConnection extends Connection{
}
这样MyConnection.getClass().getInterfaces()得到是com.mysql.jdbc.Connection,所以下面代码将不会导致转换错误
Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), MyConnection.getClass().getInterfaces(), hander);