JAVA动态代理浅析

首先要说明的是,Java语言是不支持在运行时对类进行修改的,而支持运行时修改类的语言就可以采用声明式编程了,这里多扯一句跟声明式相对应的就是命令式编程。

那要怎么让Java支持动态性呢,在不支持修改类的情况下?

答案就是代理,可以运行时生成一个新的类,这个新的类就是我们要修改为的目标类,然后这个代理类代理原先的类,执行相应的功能,就好像原先的类改变了一样。

那么Java是怎么实现代理的呢,直白的讲就是怎么创建一个新类去代理一个类呢?

使用代理我们要用到Java提供两个非常重要的东西,

一个是Proxy类

另一个是InvocationHandler接口

两位大佬都在java.lang.reflect包中,也就是反射包,这里还没剖析源码,先看一下怎么实现代理吧。

假如我现在有一个HelloWorld类,这个类代码如下

package dailyprg0713;

public class HelloWorld {
    public void say(){
        System.out. println( "Hello World !");
    }
}

它有个say()方法,我们测试一下

package dailyprg0713;

public class Test {
    public static void main( String[] args) {
        new HelloWorld(). say();
    }
}

打印结果如下


没毛病

那么问题来了,如果想在运行时“修改”这个类,让他多打印一句“Hello Java World !”怎么办?

现在我们就来试一下代理,为了让调用方感觉不到代理的存在,我们应面向接口,将say()这个方法放进接口里

package dailyprg0713;

public interface IHelloWorld {
     public void say();
}

然后去实现这个接口

public class HelloWorld implements IHelloWorld{
    public void say(){
        System.out. println( "Hello World !");
    }
}

接下来就需要去编写一个调用处理器了,这个调用处理器就是实现了InvocationHandler接口的类对象,

这个接口只有一个方法invoke(),它的特点就是,

当我们无论何时调用代理对象的方法,比如调用say()方法,调用处理器的invoke()方法就会被调用,相当于拦截了真正的方法调用,这样我们就可以在这个方法里添加或者修改我们要实现的功能了。

package dailyprg0713;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class HelloWorld implements IHelloWorld{
    public void say(){
        System.out. println( "Hello World !");
    }
}

class HelloHandler implements InvocationHandler{
    private Object target;
    public HelloHandler( Object target){
        this.target = target;
    }
    public Object invoke( Object proxy, Method method, Object[] args) throws Throwable{
        Object result = method. invoke(target, args);
        System.out. println( "Hello Java World");
        return result;
    }
}

相应的也要在main里面创建代理类

package dailyprg0713;

import java.lang.reflect.Proxy;

public class Test {
    public static void main( String[] args) {
        IHelloWorld hw = new HelloWorld();
        HelloHandler handler = new HelloHandler(hw);
        IHelloWorld proxy = (IHelloWorld)Proxy. newProxyInstance(Thread. currentThread()
            . getContextClassLoader(),hw. getClass(). getInterfaces(), handler);
        proxy. say();
    }
}

我们测试一下


确实多打印了一句,而且我们没有修改HelloWorld类,用的也是say()方法。

我们可以用代理的方式给原有的类添加事务和日志。



猜你喜欢

转载自blog.csdn.net/whut2010hj/article/details/81037521