Java中的动态代理机制

最近在看Spring的AOP, 发现它是通过动态代理来实现的,就顺便看了一下动态代理的一些例子,来写一写自己的理解。

首先动态代理有两个重要且必须用到的东西:
 1. InvocationHandler(接口, 每个动态代理类必须实现这个接口) 
 2. Proxy(类, 用来动态创建一个代理对象的类)

InvocationHandler接口中有一个invoke方法。在代理实例上处理方法调用并返回结果。当与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
- proxy 正在被代理的对象真实实例
- method 指代该代理对象实例的正在调用的某个方法对象
- args 指代该方法对象接受的参数

Proxy类中有一个newProxyInstance()方法。该方法是用于创建动态代理类和实例的静态方法.返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

下面是一段简易的动态代理机制代码, 一个加减乘除的例子。

首先有一个简单的接口类
public interface ArithmeticCalculator {
public abstract int add(int i, int j); //加
public abstract int sub(int i, int j); //减
public abstract int mul(int i, int j); //乘
public abstract int div(int i, int j); //除
}

第二个类就是接口的实现类了。很简单

public class ArithmeticCalculatorImp implements ArithmeticCalculator{

    @Override
    public int add(int i, int j) {
        int tmp = i+j;
        return tmp;
    }

    @Override
    public int sub(int i, int j) {
        int tmp = i-j;
        return tmp;
    }

    @Override
    public int mul(int i, int j) {
        int tmp = i*j;
        return tmp;
    }

    @Override
    public int div(int i, int j) {
        int tmp = i/j;
        return tmp;
    }

}

第三个类就是动态代理类了

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class ArithmeticCalculatorProxy {

    //要代理的对象
    private ArithmeticCalculator target;

    public ArithmeticCalculatorProxy(ArithmeticCalculator target0){
        this.target = target0;
    }
    public ArithmeticCalculator getProxy(){
        ArithmeticCalculator proxy = null;

        //代理对象由哪一个类加载器负责加载
        ClassLoader loader = target.getClass().getClassLoader();
        //代理对象的类型, 即其中有哪些对象
        Class[] interfaces = new Class[]{ArithmeticCalculator.class};
        InvocationHandler h = new InvocationHandler() {
            /**
             * proxy: 正在返回的那个代理对象。 一般情况下, 在invoke 方法中都不使用该对象。会死循环报栈溢出
             * method: 正在被调用的方法
             * args : 调用方法时, 传入的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String methodName = method.getName();
                System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
                Object result = null;
                try {
                    //前置通知
                    result = method.invoke(target, args);
                    //返回通知  可以访问返回值
                } catch (Exception e) {
                    // TODO: handle exception
                }
                //后置通知
                System.out.println("The method " + methodName + " ends with " + result);
                return result;
            }
        };
        proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
        return proxy;
    }
}

好了我们来测试一哈。

public class TestProxy {
    public static void main(String[] args) {

        ArithmeticCalculator target = new ArithmeticCalculatorImp();

        ArithmeticCalculator proxy = new ArithmeticCalculatorProxy(target).getProxy();

        int result = proxy.add(1, 2);
        System.out.println("-- > " + result);
        result = proxy.div(4, 2);
        System.out.println("-- > " + result);
    }
}

结果是:
The method add begins with [1, 2]
The method add ends with 3
– > 3
The method div begins with [4, 2]
The method div ends with 2
– > 2

有同学会问, invoke是什么时候调用的? 这把我问倒(:зゝ∠)了。我不能告诉你我也想过这个问题但是我也不知道, 后来看了很多帖子,才明白下面这个方法Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
做了以下几件事.
(1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy.$Proxy0类 实现了interfaces的接口,并继承了Proxy类.
(2)实例化$Proxy0并在构造方法中把BusinessHandler传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:
class Proxy{
InvocationHandler h=null;
protected Proxy(InvocationHandler h) {
this.h = h;
}

}

$Proxy0类的部分重点源码如下:

static {
   try {
    m1 = Class.forName("java.lang.Object").getMethod("equals",
      new Class[] { Class.forName("java.lang.Object") });

    m0 = Class.forName("java.lang.Object").getMethod("hashCode",
      new Class[0]);

    **m3 = Class.forName("com.ml.test.Manager").getMethod("modify",
      new Class[0]);**

    m2 = Class.forName("java.lang.Object").getMethod("toString",
      new Class[0]);

   } catch (NoSuchMethodException nosuchmethodexception) {
    throw new NoSuchMethodError(nosuchmethodexception.getMessage());
   } catch (ClassNotFoundException classnotfoundexception) {
    throw new NoClassDefFoundError(classnotfoundexception.getMessage());
   }
} //static

public $Proxy0(InvocationHandler invocationhandler) {
   super(invocationhandler);
}
public final void **modify()** {
   try {
           super.h.invoke(this, m3, null);
           return;
        } catch (Error e) {
        } catch (Throwable throwable) {
           throw new UndeclaredThrowableException(throwable);
        }
}

就是在这里调用了invoke方法来实现的。 好像有点乱- -。 下次接着补充吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值