java动态代理的相关类及使用

  之前看了好几遍java教程关于动态代理的介绍,老觉得理解不够深入,所以今天特意参考API并结合实例,记录自己学习动态代理的心得。

  所谓动态代理,我理解是在程序运行过程中动态创建接口的实例,这里的创建实例并不是直接使用new 构造器的方法实现,而是通过动态代理类代理生成。动态代理类是一个实现了一系列(一个或多个)接口的类。以此相关的还有两个概念:

  1. 代理接口:那些被动态代理类实现的接口;
  2. 代理实例:动态代理类的实例。

  每一个动态代理类都有一个相关联的 invocation handler (额,不知道该怎么翻译)对象,该对象实现了 InvocationHandler接口。 invocation handler 有什么用呢,搁置着先,看看简单的动态代理是如何实现的。

  java提供的Proxy(java.lang.reflect包下)类带有创建动态代理类或者代理类实例的静态方法,动态代理可以有以下两个实现(假如代理接口为ProxyInterface):

方法一:

//获得实现InvocationHandler接口的类的实例
InvocationHandler handler = new MyInvocationHandler(...);
//创建动态代理类proxyClass,代理的接口为Foo
Class proxyClass = Proxy.getProxyClass( Foo.class.getClassLoader(), new Class[] { Foo.class } );
//通过获取动态代理类的构造器,在使用构造器来创建对象,该对象就是实现了接口Foo的实例
Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });

方法二:

//通过Proxy的newProxyInstance方法直接生成代理实例
Foo f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),
            new Class[] { Foo.class },
            handler);

  由上可知,要实现代理,必须具备三个要素:1.实现了InvocationHandler接口的类的对象;2.代理接口的class loader;3.代理接口的Class对象的数组;

  之前提到过,每一个动态代理类都有一个与之关联的InvocationHandler接口的类的对象,该对象的作用是什么呢?让我们继续完善以上代码,创建一个实现InvocationHandler接口的类,并且在动态代理类生成对象后,执行对象的方法,完整代码如下:

//代理接口的代码
package com.lauyu;

public interface ProxyInterface
{
    public void func();
}

//动态代理类的代码
package com.lauyu;

import java.lang.reflect.*;

//实现InvocationHandler接口的类
class MyInvocationHandler implements InvocationHandler
{
    //重写InvocationHandler的invoke方法
    public Object invoke(Object proxy, Method method, Object[] args)
    {
        System.out.println("---正在执行的方法:" + method);
        return null;
    }
}

public class DynamicProxyTest 
{

    public static void main(String[] args) 
    {
        //创建hander实例
        MyInvocationHandler myHandler = new MyInvocationHandler();
        
        //通过Proxy.newProxyInstance方法创建代理实例,传入的参数有代理接口的class loader、代理接口数组、hander实例
        ProxyInterface f = (ProxyInterface)Proxy.newProxyInstance(ProxyInterface.class.getClassLoader(), 
                            new Class[] { ProxyInterface.class }, 
                            myHandler);
        
        //执行代理实例的方法
        f.func();
    }
}

  可以看到如下输出:

---正在执行的方法:public abstract void com.lauyu.ProxyInterface.func()

  程序的输出正是MyInvocationHandler类的invoke方法的方法体语句,也就是说,代理实例的调用自己的方法时,调用动作会被传递到与代理实例相关联的Hander对象的invoke方法,实际调用的是invoke方法。实际上,可以在invoke方法体内执行代理实例的方法,因为invoke方法的形参提供了执行代理实例的方法的全部信息。下面翻译一下invoke方法的形参的API解释:

完整方法签名是:Object invoke(Object proxy, Method method, Object[] args) throws Throwable

  • proxy-有方法调用动作的代理实例
  • method-Method类实例(反射相关的类),代表代理实例正在调用的接口的方法
  • args-代理实例正在调用的接口的方法的参数,类型为数组

  只要我们在invoke添加语句:method.invoke(proxy, args),就能调用代理实例的调用的方法,但是这样又会触发handler调用invoke方法,就会导致死循环,何况这里并没有实现代理接口的方法,所以调用意义不大。通常,都是在MyInvocationHandler类的invoke方法体内调用实现了代理接口的类的方法(注意这里实现代理接口的类不是代理实例)。继续完善以上代码,创建一个实现ProxyInterface接口的类,并将实现类的对象传入MyInvocationHandler。

package com.lauyu;

import java.lang.reflect.*;

//接口实现类
class ProxyInterfaceInstance implements ProxyInterface
{
    public void func()
    {
        System.out.println("---正在执行ProxyInterfaceInstance方法func");
    }
}

//实现InvocationHandler接口的类
class MyInvocationHandler implements InvocationHandler
{
    //MyInvocationHandler内部有个代理接口类型的属性
    private ProxyInterface pii;
    
    public MyInvocationHandler(ProxyInterface pii2)
    {
        //这里在构造器中初始化代理接口类型的属性
        this.pii = pii2;
    }
    
    //重写InvocationHandler的invoke方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Exception
    {
        System.out.println("---正在执行的方法:" + method);
        
        //
        method.invoke(pii);
        return null;
    }
}

public class DynamicProxyTest 
{

    public static void main(String[] args) 
    {
        ProxyInterface pii = new ProxyInterfaceInstance();
        
        //创建hander实例
        MyInvocationHandler myHandler = new MyInvocationHandler(pii);
        
        //通过Proxy.newProxyInstance方法创建代理实例,传入的参数有代理接口的class loader、代理接口数组、hander实例
        ProxyInterface f = (ProxyInterface)Proxy.newProxyInstance(ProxyInterface.class.getClassLoader(), 
                        new Class[]{ProxyInterface.class}, 
                        myHandler);
        
        //执行代理实例的方法
        f.func();
    }
}

  执行结果如下:

---正在执行的方法:public abstract void com.lauyu.ProxyInterface.func()
---正在执行ProxyInterfaceInstance方法func

  可以看到,当代理实例调用接口的方法时,myHandler对象的invoke方法被执行,且在invoke内部还执行了ProxyInterface接口类的对象的方法func。这样调用func方法有一个很好地灵活性,就是如果想要在func方法执行之前或之后进行某种业务,只要在myHandler对象的invoke方法体内添加即可,而没有必要修改func方法,从而把软件耦合降低,事实上,AOP原理就是这样。

不闻不若闻之,闻之不若见之,见之不若知之,知之不若行之
原文地址:https://www.cnblogs.com/lauyu/p/3691955.html