/**
 * 单例模式,通过DBPool连接池获取数据库连接
 * @author shi
 */
public class JdbcUtils {

private static Logger logger = Logger.getLogger(JdbcUtils.class);

//简单的数据库连接池
private static DataSource dataSource = new DBPool();
private static JdbcUtils instance; 

//构造函数私有化,为实现单例
private JdbcUtils() {
}

//注意:"锁"的方式,以及第二次"null"的判断(仔细思考:若两个线程同时到达)
public static JdbcUtils getInstance() {
if(instance == null) {
synchronized (JdbcUtils.class) {
if(instance == null) {
instance = new JdbcUtils();
}
}
}
logger.info("成功获取JdbcUtils实例");
return instance;
}

//返回连接Connection
public Connection getConnetion() throws SQLException {
return dataSource.getConnection();

}

//释放资源
public void free(ResultSet rs, Statement st, Connection conn) {
try {
if(rs != null) rs.close();
} catch (SQLException e) {
throw new JdbcException(e.getMessage());
} finally {
try {
if(st != null)st.close();
} catch (SQLException e) {
throw new JdbcException(e.getMessage());
} finally {
try {
if(conn != null) conn.close();
} catch (SQLException e) {
throw new JdbcException(e.getMessage());
}
}
}
}
}
----------------------------------------------------------------------------
/**
 * 数据库连接池
 * @author shi
 */
public class DBPool{
private static Logger logger = Logger.getLogger(DBPool.class);
private static LinkedList<Connection> connectionPool;
private static int currentCount;
private static int initCount;
private static int maxCount;
private static String sqlDriver;
private static String sqlUrl;
private static String database;
private static String user;
private static String password;
private static String encoding;
private static String url;

/*
 * 静态块初始配置
 */
static {
Properties pro = new Properties();
InputStream in = DBPool.class.getClassLoader().getResourceAsStream("db.properties");
try {
pro.load(in);
} catch (IOException e) {
logger.error("加载配置文件出现错误");
throw new RuntimeException(e.getMessage());
}

/*
 * 读取配置信息
 */
sqlDriver = pro.getProperty("sqlDriver");
database = pro.getProperty("database");
sqlUrl = pro.getProperty("sqlUrl");
user = pro.getProperty("user");
password = pro.getProperty("password");
encoding = pro.getProperty("encoding");
url = sqlUrl + database + "?user=" + user + "&password=" + password + "&" + encoding;
initCount = Integer.parseInt(pro.getProperty("initCount"));
maxCount = Integer.parseInt(pro.getProperty("maxCount"));
try {
Class.forName(sqlDriver);
} catch (ClassNotFoundException e) {
logger.error("加载驱动出现错误");
throw new RuntimeException(e.getMessage());
}
}
/*
 * 包访问权限的构造函数,尽量消除其它类对DBPool的依赖
 */
DBPool() {
connectionPool = new LinkedList<Connection>();
for(int i=0; i<initCount; i++) {
try {
Connection conn = DriverManager.getConnection(url);
// 动态代理
DynamicConnProxy dcp = new DynamicConnProxy(conn, this);
Connection c = dcp.bind();
connectionPool.addLast(c);
currentCount++;
} catch (SQLException e) {
logger.error("获取数据库连接出现错误");
throw new RuntimeException(e.getMessage());
}
}
}
/*
 * 获取数据库连接
 */
public Connection getConnection(){
synchronized (connectionPool) {
if(connectionPool.size() > 0) {
return connectionPool.removeFirst();
} else if(currentCount < maxCount) {
Connection c = null;
try {
DynamicConnProxy dcp = new DynamicConnProxy(DriverManager.getConnection(url), this);
c = dcp.bind();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage());
}
currentCount++;
return c;
} else {
throw new RuntimeException("数据库负载超荷");
}
}
}
/*
 * 关闭数据库连接
 */
public void close(Connection conn) {
connectionPool.addLast(conn);
}
}
----------------------------------------------------------------------------
/**
 * 动态代理的模式
 * @author shi
 */public class DynamicConnProxy implements InvocationHandler {
private Connection warpedConn;
private Connection realConn;
private DBPool dbpool;
DynamicConnProxy(Connection conn, DBPool dbpool) {
this.realConn = conn;
this.dbpool = dbpool;
}
public Connection bind() {
this.warpedConn = (Connection) Proxy.newProxyInstance(
DynamicConnProxy.class.getClassLoader(),
new Class[] { Connection.class }, this);
return this.warpedConn;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable { String methodName = method.getName();
if ("close".equals(methodName)) {
dbpool.close(warpedConn);
return null;
} else {
return method.invoke(realConn, args);
}
}
}上面是利用动态代理的一个小例子。但“动态代理”的内部机制是怎样实现的呢?还是通过“反射”吗?请大家指点。

解决方案 »

  1.   

    动态代理远远超过反射的能力范围,JDK 的动态代理应该是自动生成一个代理类。而 Cglib 框架的动态代理是使用 ASM 这个字节码工具,直接修改字节码而来的。PS:你的单例方法有问题,JDK 中的双检锁无效,除非 JDK 1.5 以上版本在变量上修饰 volatile 时双检锁才有效果。
      

  2.   

    你这些代码,我看得稀里糊涂的private static DataSource dataSource = new DBPool();DBPool 并没有实现 DataSource 接口,可以这样用么?    private static LinkedList<Connection> connectionPool;
        private static int currentCount;
        private static int initCount;
        private static int maxCount;
        private static String sqlDriver;
        private static String sqlUrl;
        private static String database;
        private static String user;
        private static String password;
        private static String encoding;
        private static String url;还有这一堆的东西,也没看懂为什么要弄成 static 的?要弄动态代理千万别拿连接池来做试验,连接池是个非常复杂的东西,并不是那么简单的,你的这个连接池只解决了一个 Connection 的 close 问题。当用户获得不到连接池,应该使用 wait 方法,或者是 wait(timeout) 在那里等着,超时后再抛出异常,这样会比较好一些。另外,你这里也没有 Connection 的健康状况检查,数据库服务器会把一些占用时间很长的连接主动断开,如果是这样的话,长时间使用会导致很多连接无效。再者,如果网络连接断开,也没看到有将池中连接重新刷新的代码。
      

  3.   


    我说肯定用到了反射 没说只用了反射动态代理的本质就在这个方法里
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                        proxyName, interfaces);
    这个proxyName难道不是反射得到的吗?然后通过这个名字和接口生成java源代码 编译成字节码文件 调入虚拟机 实例化 返回对象 就这么简单
      

  4.   


    我做了个测试 调用ProxyGenerator.generateProxyClass(proxyName, interfaces);方法得到字节码 写到文件中(.class文件) 然后通过JD反编译 得到代理类(.java文件)这个类是这样的
    public final class MyProxyClass extends Proxy implements MyInterface
    有一个构造方法
    public MyProxyClass(InvocationHandler paramInvocationHandler)
        throws {
        super(paramInvocationHandler);
      }所以我们通常用动态代理是这么用的:写一个类,比如名字叫MyHandler,实现InvocationHandler接口.
    然后把MyHandler对象作为参数传给Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);这个方法.
    这个方法会调用ProxyGenerator.generateProxyClass(proxyName, interfaces);方法生成一个代理类.然后通过反射调用上面那个构造方法进行实例化,参数就是自己写的MyHandler的对象.
    而这个代理类的任何方法实际上都是调用这个MyHandler的invoke方法,只是不同方法的参数不同.所以无论你调用什么方法,实际上都在调用你写的invoke方法.
    这样你就可以在invoke方法里自由发挥,前插后插,随便你怎么插,这就是AOP.
      

  5.   

    你认为简单就简单吧,反正你也一口咬定了,我也无话可说了。JDK 的动态代理有一定的局限性,被代理的类必须有个接口,否则无法进行代理。
      

  6.   

    非常感谢“火龙果”和“haojia0716”的指点!的确和“火龙果”说的那样存在的问题太多。希望几位能够把自己的理解分享一下。真诚感谢。---------------------------1.还有这一堆的东西,也没看懂为什么要弄成 static 的? 这个的确没必要。改正。2.DBPool 并没有实现 DataSource 接口,可以这样用么?这是自己发帖的时候,修改了DBPool,另外一边忘了改。 像连接池的“错误机制”、“算法”等等问题,是更重要的。-----------------------自己目前的理解程度和haojia0716所做的测试一样。希望火龙果能够给更多的指点!谢谢
     
      

  7.   


    这里并没有生成 java 源代码,也没有什么编译之说,而是根据 JVM 规范直接生成 class 类的字节码,再通过 native 方法加载这个类。而且在非调试状态下连 .class 文件都不会生成的,除非 System Property 的 sun.misc.ProxyGenerator.saveGeneratedFiles 的值为 true 才会产生 .class 文件。作为一个 Proxy 的代理类,其代理对象的类名为代理类第一个 public 接口的包名加上 $Proxy 再加上数字。比如被代理对象第一个 public 接口为 net.csdn.j2se.Service 的话,那么代理对象的类名为 net.csdn.j2se.$Proxy0这个代理对象会被 Proxy 缓存,如果多次使用 Proxy.newProxyInstance 进行同一个代理的话,只会产生一个 $Proxy0 的类名,如果还有其他代理的话会是 $Proxy1, $Proxy2 依次下去。但是 JDK 的 Proxy 有一个局限,就是只能为接口创建代理对象,如果需要代理的某个类没有接口的话,那 JDK 将无计可施。这时我们会用到另一个动态代理的实现 Cglib,Cglib 采用 ASM 这个字节码框架,在运行时动态地为一个类创建一个代理子类,此时并不需要这个类有个接口,但由于是动态生成一个子类,因此类中的方法不能使用 final 进行修饰,否则无法创建代理对象。JDK 的动态代理是 JDK 1.3 开始引入的功能,但是当初的性能是非常差的,虽然现在性能提高不少,但在某些方面还是逊色于 Cglib,但各有优缺点,据有些研究表明 JDK 代理在创建代理对象的性能要低于 Cglib 的代理,但是 Cglib 创建代理类的性能要低于 JDK 的动态代理。根据一些最佳实践表示,如果代理对象不需要频繁的创建,比如说采用了单例模式时,可以使用 Cglib 的代理,反之应使用 JDK 的代理。关于 JDK 与 Cglib 代理性能可以看一下 TheServiceSide 的这篇讨论:

    http://www.theserverside.com/news/thread.tss?thread_id=45089
    在 Rob Harrop, Jan Machacek 所编写的 Pro Spring 一书第 6 章 All About Proxies 一节中也有 JDK 与 Cglib 的性能分析。不过该性能测试是使用 JDK 1.4 进行的。JDK 5、JDK 6 起 JDK 的动态代理性能有了明显的提高。
      

  8.   

    我当时回完帖子后就想了想,应该就是直接生成个class.不过懒得去管他了.
    cglib还是比较受欢迎点,hibernate懒加载也是用他.天亮了,我要去睡觉了.
      

  9.   

    因此动态代理跟反射的关系并不是太大。其动态生成的字节码中,会将需要代理的每一个方法作为代理对象的一个类型为 Method 的属性,便于动态代理对象进行调用。
      

  10.   

    jdk有jdk的反射 cglib有cglib的反射广义的讲反射也就是在编译时,你获取了一个指向某个在你的程序空间中并不存在的对象的引用;事实上在编译时你的程序无法获知这个对象所属的类.所以说cglib背后的机制也可以叫反射.难道不同于jdk的那种方式就不能叫反射了?