详解动态代理及其实现原理

1.什么是代理。比如(工厂,商店,客户),工厂是委托类,商店是代理类,工厂委托商店做代理来买产品,可以这样通俗理解。
2.代理的好处:(1.可以隐藏委托类的实现;2.可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理,比如买烟的时候限制年龄…可以在代理类中进行对应的操作。)
3.再讲动态代理之前,先明白静态代理是怎么实现的,若代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理。
看代码:
公共的接口Sell:

package proxy;
/**
 * @author yhl
 * 解释为什么要都实现统一接口:通常情况下,静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类,不然之间没有一点关系,更谈不上代理什么了)
 */
public interface Sell {
    void sell();//买东西
}

工厂类:

package proxy;

public class Factory implements Sell{

    @Override
    public void sell() {
        System.out.println("Factory sell...");      
    }

}

Store类:

package proxy;

public class Store implements Sell {
    //静态代理可以通过聚合来实现,让代理类持有一个委托类的引用即可。
    private Factory factory = null;

    public Store(Factory fc) {

        this.factory = fc;
    }

    @Override
    public void sell() {
        System.out.println("before"); 
            factory.sell();
        System.out.println("end"); 

    }
    //重载一个方法
    public void sell(String param) {
        if(choose(param)){
            System.out.println("before"); 
                factory.sell();
            System.out.println("end"); 
        }else{
            System.out.println("不符合要求");
        }
    }
    /*
     * 根据需求添加一个过滤功能---卖烟给学生
     * 通过静态代理,我们无需修改Factory类的代码就可以实现,只需在Store类中的sell方法中添加一个判断即可如下所示:
     */
    public boolean choose(String param) {
        boolean flag = false;
        if (!param.equals("学生")) {
            flag = true;
        }
        return flag;
    }

}

测试类:

package proxy;

public class Client {

    @Test
    public void 静态代理测试() {
        Store st = new Store(new Factory());
        st.sell();
        st.sell("学生2");
    }

}

代码看完之后:
总结静态类:照应第二个优点,可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。静态代理的局限在于运行前必须编写好代理类。
动态代理
假设需求:在执行委托类中的方法之前输出“before”,在执行完毕后输出“after”。我们还是以上面例子中的Factory类作为委托类,Store类作为代理类来进行介绍,假如使用静态代理来实现这一需求,请返回看上边的store类代码,假如说委托对象里边有很多方法,那么这样的需求添加操作就显得不合适,造成代码的臃肿,显然不符合现在的代码编写规范。由此就引出动态代理。
基本概念:在程序运行时,根据委托类及其实现的接口,动态的创建一个代理类,当调用代理类实现的抽象方法时,就发起对委托类同样方法的调用(可以讲解完代码回头再看一遍)。
涉及的技术点:1.要提供一个实现了InvocationHandler接口的实现类,并重写其invoke()方法.2.通过Proxy获取动态代理实例,查看jdk-api。
先看一个完整的动态代理例子,看到的朋友可以直接拷贝在本地运行:

package proxy.dynmic;

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

//接口
interface Human{
    void info();
    void fly();
}

//委托类
class SuperMan implements Human{
    public void info(){
        System.out.println("我是超人"); 
    }
    public void fly(){
        System.out.println("i can fly !!!");
    }
}

class HumanUtil{
    public void method1(){
        System.out.println("执行method1");
    }
    public void method2(){
        System.out.println("执行method2");
    }
}

//动态的创建一个代理类对象
class MyProxy{
    public static Object getProInstance(Object obj){
        MyInvocationHandler handle = new MyInvocationHandler();
        handle.setObject(obj);//将被代理对象传进去进行实例化(委托类)
        //创建一个obj对象对应的代理类
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handle);
    }
}

/**
 * handle
 * 处理器-中介类,实现了InvocationHandler这个接口的中介类用做“调用处理器”,”拦截“对代理类方法的调用。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,
 * 代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数,这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用
 */
class MyInvocationHandler implements InvocationHandler{

    Object obj = null;//被代理对象的声明(委托类)

    //实例化对象
    public void setObject(Object o){
        this.obj=o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //proxy参数传递的即是代理类的实例
        System.out.println("name:"+proxy.getClass().getName());
        //HumanUtil hu= new HumanUtil();//想要在执行1和2之间动态的执行插入的方法
        //hu.method1();
        Object returnval = method.invoke(obj, args);
        //hu.method2();
        return returnval;
    }

}

public class TestAop {
    public static void main(String[] args) {
        SuperMan sm = new SuperMan();
        Object obj  = MyProxy.getProInstance(sm);//代理类对象,同样的代理类对象也是实现SuperMan接口的
        Human hm = (Human) obj;
        hm.info();//通过代理类的对象调用重写的抽象方法,实际上会转到invoke方法调用
    }
}

接下来,开始挨着解决疑点:
类 Proxy,提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。我们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器。

//动态的创建一个代理类对象
class MyProxy{
    public static Object getProInstance(Object obj){
        MyInvocationHandler handle = new MyInvocationHandler();
        handle.setObject(obj);//将被代理对象传进去进行实例化(委托类)
        //创建一个obj对象对应的代理类
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handle);
    }
}

方法的三个参数含义分别如下:
loader:定义了代理类的ClassLoder;
interfaces:代理类实现的接口列表
h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例。
对应的代码就是我们MyProxy类中的Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handle);
handle就是调用处理器,指的是MyInvocationHandler这个类。
类InvocationHandler是代理实例的—调用处理程序(handler)—实现的接口。
每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
更加通俗的解释:在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口,中介类其实就是调用处理程序。接下来我们来看看MyInvocationHandler这个中介类的类是怎么实现的:

/**
 * handle
 * 处理器-中介类,实现了InvocationHandler这个接口的中介类用做“调用处理器”,”拦截“对代理类方法的调用。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,
 * 代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数,这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用
 */
class MyInvocationHandler implements InvocationHandler{

    Object obj = null;//被代理对象的声明(委托类)

    //实例化对象
    public void setObject(Object o){
        this.obj=o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //proxy参数传递的即是代理类的实例
        System.out.println("name:"+proxy.getClass().getName());
        //HumanUtil hu= new HumanUtil();//想要在执行代理方法之前之后动态的插入1和2方法
        //hu.method1();
        Object returnval = method.invoke(obj, args);
        //hu.method2();
        return returnval;
    }

}

最后再解释一下:
从以上代码中我们可以看到,中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,看到这里是不是觉得似曾相识?通过聚合方式持有委托类对象引用,把外部对invoke的调用最终都转为对委托类对象的调用。这不就是我们上面介绍的静态代理的一种实现方式吗?实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类; 代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理
至此,代理的模式已经给大家讲解完了,但是在此提出一个疑问,为什么在测试类中执行hm.info()时,实际上会转到invoke方法调用。当你执行完测试代码后会看到:
这里写图片描述
如图片所示,代理类的父类是Proxy,实现了Human接口,但是具体的代理类是怎么生成的,从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:

public static Object newProxyInstance(ClassLoader loader,  
        Class<?>[] interfaces,  
        InvocationHandler h)  
throws IllegalArgumentException  
{  
    if (h == null) {  
        throw new NullPointerException();  
    }  

    /* 
     * Look up or generate the designated proxy class. 
     */  
     //这里是生成class的地方,获得与指定类装载器和一组接口相关的代理类类型对象 
    Class cl = getProxyClass(loader, interfaces);  

    /* 
     * Invoke its constructor with the designated invocation handler. 
     */  
    try {  
           /* 
            * Proxy源码开始有这样的定义: 
            * private final static Class[] constructorParams = { InvocationHandler.class }; 
            * cons即是形参为InvocationHandler类型的构造方法 
           */  
            // 调用代理对象的构造方法(也就是$Proxy0(InvocationHandler h))
        Constructor cons = cl.getConstructor(constructorParams);  
        // 生成代理类的实例并把MyInvocationHandler的实例传给它的构造方法
        return (Object) cons.newInstance(new Object[] { h });  
    } catch (NoSuchMethodException e) {  
        throw new InternalError(e.toString());  
    } catch (IllegalAccessException e) {  
        throw new InternalError(e.toString());  
    } catch (InstantiationException e) {  
        throw new InternalError(e.toString());  
    } catch (InvocationTargetException e) {  
        throw new InternalError(e.toString());  
    }  
}  

看代码,newProxyInstance方法做了以下几件事. (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类Proxy0,Proxy0类实现了interfaces的接口,并继承了Proxy类.
(2)实例化Proxy0并在构造方法中把委托类(handler–MyInvocationHandler)传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:
Proxy0构造器:

 public $Proxy0(InvocationHandler invocationhandler) {  
        super(invocationhandler);  
    }

父类Proxy构造器:

class Proxy{  
    InvocationHandler h=null;  
    protected Proxy(InvocationHandler h) {  
        this.h = h;  
    }  
    ...  
} 

看下生成的动态代理类(类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据):

public final class $Proxy0 extends Proxy implements Human{  
    private static Method m1;  
    private static Method m0;  
    private static Method m3;  
    private static Method m2;  

    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("***.SuperMan").getMethod("info",  
                    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);  
    }  

    @Override  
    public final boolean equals(Object obj) {  
        try {  
            return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  

    @Override  
    public final int hashCode() {  
        try {  
            return ((Integer) super.h.invoke(this, m0, null)).intValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  /** 
     * 这个方法是关键部分 
     */ 
    public final void info() {  
        try {  
        // 实际上就是调用MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法,第二个问题就解决了
            super.h.invoke(this, m3, null);  
            return;  
        } catch (Error e) {  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  

    @Override  
    public final String toString() {  
        try {  
            return (String) super.h.invoke(this, m2, null);  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
}  

总结
一个典型的动态代理创建对象过程可分为以下四个步骤:
1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
5、重点解决疑惑:
生成的$Proxy0继承Proxy类实现Human接口,实现的Human的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))
最后
写到这里,基本上关于动态代理的相关问题就基本解释完成了,这篇文档的编写,也参考了不少相关文档,自己也看了一些视频介绍,自己会在下边列出参考文献,如果有读到这篇文章的朋友们,要是还有疑问的话,可以留言一起探讨,共同进步!!!如果想要再深一层次的去理解,就要熟读源码了,这个随着以后能力的提升再次拜读。。。相关切面Aop我在这里就不再多说了,理解起来也比较简单,可以再参考一些其他的,就到这吧。
参考文献:
http://blog.csdn.net/zcc_0015/article/details/22695647
http://rejoy.iteye.com/blog/1627405?page=2
https://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html

  • 11
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值