java代理模式-动态代理学习

尊重原创:http://blog.csdn.net/bingju328/article/details/53028729

前言:

通过上一篇 java代理模式-静态代理学习 对静态代理的学习,我们知道静态代理模式中的每个类在编译后就会生成一个class文件,即代理类所实现的方法在编译完成后就都被固定了。如果用这种代理方式,当我们在代理多个对象时,就需要为每个实体类都编写一个新的代理类,这样势必会导致项目中的类越来越多,比如说:在 java代理模式-静态代理学习 一篇中存在的 房天下的房东类:FTXRentSubject(具体主题角色)所对应的代理类为房天下公司类:ProxyFTXSubject(代理对象角色) , 此时若添加一个新的 链家的房东类:LjRentSubject(具体主题角色)必须为这个类新写一个代理类 链家公司类:ProxyLjSubject(代理对象角色),这个时候有人说了,这还好,就加一个新的代理类我能接受啊。那如果需求是有100个具体主题需要被代理呢~~~是不是要疯了。。~_~|||。

那有木有办法在程序运行的时候动态的创建代理类呢?肯定有了,那就是本文将要了解的动态代理技术。为了更好的理解,动态代理要和上一篇的静态代理对比着学习,所以例子依然是在上一篇 文章例子的基础下修改的,不太熟悉的静态代理的同学可以先看一下上一篇 java代理模式-静态代理学习

动态代理介绍:
  • 动态代理可以根据具体主题对象在运行时动态的创建代理类,即一个代理类可以代理多个不同的具体主题对象。

动态代理主要涉及的类:

  • Proxy : 这个类提供了动态创建代理对象的方法,它也是所有通过它的方法创建的代理对象的父类.
    主要方法有:public static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);

    • 第一个参数是被代理的对象的类加载器 。
    • 第二个参数是被代理的对象实现的接口集合 。
    • 第三个参数是 InvocationHandler接口的一个实现类,当被代理的具体对象的方法被调用的时候,会调用InvocationHandler的实现类中的invoke()方法。
  • InvocationHandler:一个代理实例调用处理的接口。
    主要方法有:public Object invoke(Object proxy, Method method, Object[] args);

    • 第一个参数是被调用方法的代理实例。
    • 第二个参数是需要代理的方法(即代理实例中要调用的方法)。
    • 第三个参数是第二个参数 method 中所需传入的参数。
  • InvocationHandler、Proxy、被代理的目标对象三者的关系:

    通过Proxy方法返回一个目标对象的代理实例,这个代理实例动态的实现了目标对象所实现的接口,同时也持有InvocationHandler的一个实现类的引用,当代理实例调用目标对象中的方法的时候,这个方法的调用在运行时被编码并且委派给 InvocationHandler的一个实现类中的invoke()方法(就是代理实例调用代理方法的时候其实是调用的InvocationHandler中的invoke()方法),由invoke() 来统一的处理目标对象对方法的调用。

实现代码如下:

抽象主题

/**
 * 抽象主题,定义主要功能的接口
 * 功能:rent() 租房
 * */
public interface RentSubject {

    public String rent(String operation);

}

(房天下公司房子的房主)第一个具体主题

/**
 * (房天下公司房子的房主)具体主题,是对抽象主题的实现
 * */
public class FTXRentSubject implements RentSubject {
    public String rent(String operation) {
        //房主具体的操作
        System.out.println("FTXRentSubject--"+operation);
        return operation;
    }
}

(房天下公司)第一个代理对象角色

/**
 * (房天下公司)代理对象角色,替目标对象(即具体主题,{@link FTXRentSubject})来做相关的操作
 * */
public class ProxyFTXSubject implements RentSubject {
    private FTXRentSubject ftxRentSubject;

    public ProxyFTXSubject(FTXRentSubject ftxRentSubject) {
        this.ftxRentSubject = ftxRentSubject;
    }

    public String rent(String operation) {
        //房东租房前的操作
        System.out.println(operation+"-before");
        //调用目标对象的方法来操作
        //此调用方式实质上也是类ProxyFTXSubject和FTXRentSubject之间的一种"组合方式" 的调用
        //主要作用是:在需求变更的时候操作代理对象ProxyFTXSubject而不用改变FTXRentSubject类
        //从而达到了解耦的效果
        ftxRentSubject.rent(operation);
        //房东租房后的操作
        System.out.println(operation+"-after");
        return operation;
    }

}

(链家公司房子的房主)第二个具体主题

/**
 * (链家公司房子的房主)具体主题,是对抽象主题的实现
 * */
public class LjRentSubject implements RentSubject {
    public String rent(String operation) {
        //房主具体的操作
        System.out.println("LjRentSubject--"+operation);
        return operation;
    }

}

(链家公司)第二个代理对象角色

/**
 * (链家公司)代理对象角色,替目标对象(即具体主题,{@link LjRentSubject})来做相关的操作
 * */
public class ProxyLjSubject implements RentSubject {
    private LjRentSubject ljRentSubject;

    public ProxyLjSubject(LjRentSubject ljRentSubject) {
        this.ljRentSubject = ljRentSubject;
    }

    public String rent(String operation) {
        //房东租房前的操作
        System.out.println(operation+"-before");
        //调用目标对象的方法来操作
        //此调用方式实质上也是类ProxyFTXSubject和FTXRentSubject之间的一种"组合方式" 的调用
        //主要作用是:在需求变更的时候操作代理对象ProxyFTXSubject而不用改变FTXRentSubject类
        //从而达到了解耦的效果
        ljRentSubject.rent(operation);
        //房东租房后的操作
        System.out.println(operation+"-after");
        return operation;
    }

}

客户端类

/**
 * 客户端类
 * */
public class Client {

    public static void main(String[] args){
        //房天下的房东客户不能直接找房东租
        FTXRentSubject ftxRentSubject = new FTXRentSubject();
        ProxyFTXSubject proxyFTXSubject = new ProxyFTXSubject(ftxRentSubject);
        //客户给房天下公司发出需求:租房子
        proxyFTXSubject.rent("房天下客户租房子");
        //链家的房东客户不能直接找房东租
        LjRentSubject ljRentSubject = new LjRentSubject(); 
        ProxyLjSubject proxyLjSubject = new ProxyLjSubject(ljRentSubject);
        proxyLjSubject.rent("链家客户租房子");

        //从现在开始所有所有客户都要办理居住证才可以租房子
        proxyFTXSubject.rent(new HandleJuZhuPermit("房天下客户租房子").handlePermit("先办理居住证"));
    }
}

上面这个客户端类是静态代理的操作调用,对目标对象FTXRentSubject、LjRentSubject ,我们需要新建两个代理类 ProxyFTXSubject、ProxyLjSubject 来分别代理不同的目标对象,就像文章开头所说那样,如果目标对象有10个20个,那我们需新建10个20个代理类来分别代理不同的目标对象,这样势必会导致代码冗余,所以才需要动态代理的。

下面是动态代理的相关类:

InvocationHandler接口的实现类

/**
 * InvocationHandler 这个接口是被Proxy 实例实现的接口,通过Proxy 把每个被代理的
 * 对象和InvocationHandler联系起来,当被代理对象的方法被调用的时候会调用 被实现的{@code invoke}
 * 方法。
 * 此类就是InvocationHandler的类
 * */
public class RealInvocationHandler implements InvocationHandler{
//  private RentSubject rentSubject;
    private Object object1;

    public RealInvocationHandler(Object rentSubject) {
        this.object1 = rentSubject;
    }
    /**
     * @param Object proxy 被代理的角色对象
     * 
     * @param Method method 被代理的角色对象将要调用的方法
     * 
     * @param Object[] args 被代理的角色对象调用的方法的参数 集合
     * */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy:"+proxy.getClass().getName());  
        System.out.println("method:"+method.getName());  

        System.out.println(((args != null && args.length > 0) ? args[0] : "租房子-")+"before");
        Object object = method.invoke(object1, args);
        System.out.println(((args != null && args.length > 0) ? args[0] : "租房子-")+"after");
        return object;
    }

}

动态代理的客户端操作类

/**
 * 客户端类
 * */
public class Client {

    public static void main(String[] args){

        //动态代理FTXRentSubject目标对象
        System.out.println("动态代理--------------");
        FTXRentSubject ftxRentSubject = new FTXRentSubject();
        /**
         * newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
         * 
         * ClassLoader loader 被代理的对象的类加载器
         * Class<?>[] interfaces 被代理的对象实现的接口集合
         * InvocationHandler h 当被代理对象的方法被调用的时候会调用 被实现的InvocationHandler中的 invoke()方法。
         * 根据传入的参数返回一个动态生成的 代理类实例对象
         * @return 
         * */
        RentSubject ftxsubject = (RentSubject) Proxy.newProxyInstance(ftxRentSubject.getClass().getClassLoader(), 
                ftxRentSubject.getClass().getInterfaces(), new RealInvocationHandler(ftxRentSubject));
        //调用此方法实质上是调用了 实现的InvocationHandler中的 invoke()方法
        ftxsubject.rent("房天下客户租房子");

        //动态代理LjRentSubject目标对象
        System.out.println("动态代理--------------");
        LjRentSubject ljRentSubject = new LjRentSubject();

        RentSubject ljsubject = (RentSubject) Proxy.newProxyInstance(ljRentSubject.getClass().getClassLoader(), 
                ljRentSubject.getClass().getInterfaces(), new RealInvocationHandler(ljRentSubject));
        //调用此方法实质上是调用了 实现的InvocationHandler中的 invoke()方法
        ljsubject.rent("链家客户租房子");
    }
}

上面这个客户端类是动态代理的操作调用,对目标对象FTXRentSubject、LjRentSubject ,只需用同一个方法Proxy.newProxyInstance();这个方法返回一个接口RentSubject 的一个实现类,当代理实例调用目标对象中的rent()方法的时候,这个方法的调用在运行时被编码并且委派给 InvocationHandler的一个实现类(RealInvocationHandler)中的invoke()方法(就是代理实例调用代理方法rent()的时候其实是调用的InvocationHandler中的invoke()方法),由invoke() 来统一的处理目标对象对方法的调用。

后记:

以上就是我学习动态代理模式的相关记录,设计模式只是整个程序大厦中的基础,基础牢固了,再来了解一些基于相应的设计模式编写的开源框架就会比较容易了。比如:了解完了动态代理,这个时候再来看Retrofit的源码就相对容易了,其实Retrofit框架主要的思想就是通过动态的代理每个ApiService的接口,然后在InvocationHandler中的invoke()方法中做相应的处理。这理只做简单的说明,具体分析放在后续的笔记中进行说明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值