Java中动态代理的实现

来源:互联网 发布:实体设计软件 编辑:程序博客网 时间:2024/05/16 03:50
    通过上一篇《Java中代理模式的实现步骤》,我们已经掌握了如何运用代理模式进行权限设计等相关需求的编程方式,在有些情况下,我们会有特殊的需求,比如说我们需要实现一个数据库的连接池,连接池给调用者提供连接(Connection)的对象,使用者在用过Connection对象之后需要归还连接,而Connection中有一个方法叫close(),此时我们希望用户在使用完对象后直接调用close()方法进行连接的归还,这样有两个好处,第一,用户不必关心使用哪个方法归还连接,甚至用户不需要知道归还连接这回事;第二,不必再多写一个方法,给调用带来麻烦,用户的感觉好像就是使用完连接之后直接close掉,这样对用户很透明。那么如何实现这个功能呢?
    我们可以使用上一篇文章中讲述的代理模式来完成这个任务,我们可以编写一个代理类实现Connection接口,然后在连接池送出连接对象的时候,我们实际送出的是代理类的对象,当代理类对象的方法被调用的时候,代理类对象通过对真实的Connection的对象的相应功能进行调用来完成具体功能,但是如果被调用的方法是close的话,代理类中的方法不是去真的调用Connection中的close方法,而是完成连接归还这个动作,这样就当到我们预定的需求了,但是当你真的开始使用这个方法开始编写程序的时候,你会发现为了完成这个任务,你需要实现Connection中的很多个方法,而这样做的目的只是为了一个close方法的功能重写,这对程序编写来讲带来了很多的麻烦,为此,我们提出了动态代理的实现。我们不必专门编写一个类去实现Connection接口,希望能够通过在运行过程中动态产生一个代理类的对象,这个代理类对象负责监视被调用的方法,如果是close方法,则处理连接归还的任务,否则就将调用传递给Connection对象的相关方法去处理。接下来我们通过实例来表达具体的实现步骤。
    首先我们给出连接池实现类的部分示范代码:
public class ConnectionPool {
    /*
    ......此处省略很多代码
    */

    public Connection getConnection() {
        Connection con;
        /*
        ......此处省略部分代码;
        ......取得真实的Connection的对象记录在con中
        */

        return con;
    }
}
    在上面的代码示例中,我们可以看到getConnection方法通过取得连接对象并返回给调用者,这里我们需要修改如上代码,我们不能把Connection的对象直接返回给调用这个,我们需要送出去的是一个代理类的对象。而java中为我们提供了动态创建代理类对象的工具java.lang.reflect.Proxy类,可以使用这个类中的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)来创建动态代理对象,这个方法需要几个参数,第一个参数是类加载器,这个参数没有什么问题,第二个参数就是要求动态代理类对象实现的所有的接口,第三个参数是用于处理回调的对象,当动态代理类对象中的方法被调用的时候,回调处理类的对象会收到处理通知,我们可以判断被调用的是什么方法来做出相关的处理,这个回调处理类需要实现InvocationHandler接口,接下来看一下这个接口中的方法:
 Object invoke(Object proxy, Method method, Object[] args)
    从方法的参数,我们可以轻松地看出,第一个参数是被调用的代理类对象的引用,第二个参数是被调用的方法的实例描述,可以通过这个参数判断出被调用的是哪个方法,第三个参数是调用时出入的方法的参数,有了这样的工具,我们可以修改上面的ConnectionPool类的内容了。
public class ConnectionPool {
    /*
    ......此处省略很多代码
    */

    /*
    * 我们通过一个内部类来实现InvocationHandler
    */
    private class ConnectionHandler implements InvocationHandler {
        //这个属性用户记录真实的Connection对象
        private Connection con;
        public ConnectionHandler(Connection con) {
            this.con = con;
        }

        public Object invoke(Object proxy, Method method, Object[] args) {
            if(Method.getName().equals("close") {
                //处理归还连接的任务
            }
            else {
                method.invoke(con, args);
            }
        }
    }

    public Connection getConnection() {
        Connection con;
        /*
        ......此处省略部分代码;
        ......取得真实的Connection的对象记录在con中
        */

        Connection tc = Proxy.newProxyInstance(Connection.class.getClassLoader(), new Class[]{Connection.class}, new ConnectionHandler(con));

        return tc;
    }
}

    好了,到这里,我们已经成功地把连接池中的代理模式的使用替换成了动态代理的实现。在你需要地方大胆去使用吧,祝你成功。

原创粉丝点击