浅谈Java代理模式-----------静态代理和动态代理

 

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。

本文是对这篇博客(http://www.importnew.com/26116.html)进行学习的,通过自己理解,对其中的一些代码加上自己的注解,更方便大家学习。

可以用图来表示:

代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象。

 

代理的角色:

第一种:抽象角色,声明真实对象和代理对象的共同接口。

第二种:代理角色,代理对象角色内部含有对真实对象的引用,从而可以操纵真实的对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象,同时代理对象可以在执行真实对象操作时,附加其他的操作,相对于对真实对象封装。

第三种:真实角色,代理所代理的真实角色,是我们要引用的对象。

 

举个例子,小张是一个普通的码农,有一个产品经理小李,有一个客户有需求想要找小张。

对照上面定义,小张可以映射成真实角色,小李是代理角色,这两个人都是程序员,都可以抽象成码农。

1.1静态代理

1.抽象角色

基于面向对象的思想,首先定义一个码农接口,它有一个实现用户需求的方法

 

public interface ICoder {

 

    public void implDemands(String demandName);

 

}

 

2.真实角色

我们假设小张是JAVA程序员,定义一个JAVA码农类,他通过JAVA语言实现需求。

public class JavaCoder implements ICoder{

 

    private String name;

 

    public JavaCoder(String name){

        this.name = name;

    }

 

    @Override

    public void implDemands(String demandName) {

        System.out.println(name + " implemented demand:" + demandName + " in JAVA!");

    }

}

 

3.代理角色

委屈一下产品经理,将其命名为码农代理类,同时让他实现ICoder接口。

public class CoderProxy implements ICoder {


    //真实角色,但现在是一个抽象接口
    private ICoder coder;

    public CoderProxy(ICoder coder){
        this.coder = coder;
    }

    @Override
    public void implDemands(String demandName) {
        coder.implDemands(demandName);
    }
}

上面一个接口,两个类,就实现了代理模式。Are you kidding me?这么简单?是的,就是这么简单。 我们通过一个场景类,模拟用户找产品经理增加需求。

public class Customer {

 

    public static void main(String args[]){

        //定义一个java码农

        ICoder coder = new JavaCoder("Zhang");

        //定义一个产品经理

        ICoder proxy = new CoderProxy(coder);

        //让产品经理实现一个需求

        proxy.implDemands("add user manageMent");

    }

}

 

运行程序,结果如下:

Zhang implemented demand:add user manageMent in JAVA! 

产品经理充当了程序员的代理,客户把需求告诉产品经理,并不需要和程序员接触。

 

静态代理总结:

  • 职责清晰 真实角色只需关注业务逻辑的实现,非业务逻辑部分,后期通过代理类完成即可。
  • 高扩展性 不管真实角色如何变化,由于接口是固定的,可以做到在不修改目标对象的功能前提下,代理类无需做任何改动。

缺点:

  • 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。

如何解决静态代理中的缺点呢?答案是可以使用动态代理方式

--------------------------------------------------------------------------------------------------------------------

1.2.动态代理

动态代理有以下特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理。

JDK中生成代理对象的API------java中文API 1.8(https://blog.fondme.cn/apidoc/jdk-1.8-google/)
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

  • ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
  • Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

 

与静态代理相比,抽象角色、真实角色都没有变化。变化的只有代理类。因此,抽象角色、真实角色,参考ICoder和JavaCoder。

在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,也叫动态代理类,这个类被要求实现InvocationHandler接口:

public class CoderDynamicProxy implements InvocationHandler {
    //被代理的实例
    private ICoder coder;

    public CoderDynamicProxy(ICoder _coder){
        this.coder = _coder;
    }

    /*jdk的动态代理,被代理的对象必须实现接口
    * 第一个参数是要被代理的类
    * 第二个参数method是被代理对象的接口方法
    * 第三个参数是被代理对象接口方法的参数
    *
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //System.out.println(System.currentTimeMillis());
        Object result = method.invoke(coder, args);
        //System.out.println(System.currentTimeMillis());
        return result;
    }
}

 

当我们调用代理类对象的方法时,这个“调用”会转送到中介类的invoke方法中,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。

 

我们通过一个场景类,模拟用户找产品经理更改需求。

public class DynamicClient {

    public static void main(String args[]){
        //要代理的真实对象
        ICoder coder = new JavaCoder("Zhang");
        //创建动态代理类实例
        InvocationHandler handler = new CoderDynamicProxy(coder);

        //动态产生一个代理类,第一个参数是获得被代理的对象的类加载器,第二个参数获得被代理对象的类的所有接口,第三个参数是自定义动态代理类的对象中介类
        ICoder proxy = (ICoder) Proxy.newProxyInstance(coder.getClass().getClassLoader(), coder.getClass().getInterfaces(), handler);
        //通过代理类,执行impleDemands方法,会被转送到中介类的invoke方法中;
        proxy.implDemands("Modify user management");
    }
}

 

执行结果如下:

Zhang implemented demand:Modify user management in JAVA!

通过上述代码,就实现了,在执行委托类的所有方法前、后打印时间。还是那个熟悉的小张,但我们并没有创建代理类,也没有时间ICoder接口。这就是动态代理。

总结

总结一下,一个典型的动态代理可分为以下四个步骤:

  1. 创建抽象角色 (写一个接口)
  2. 创建真实角色 (创建被代理对象)
  3. 通过实现InvocationHandler接口创建中介类
  4. 通过场景类,使用newProxyInstance方法来动态生成代理类
  5. 执行代理类的方法

ps:如果想深入了解为什么通过proxy.newProxyInstance就能把代理类的方法调用,分派给中介类的invoke方法,可以看这篇博客http://www.importnew.com/26116.html(从“源码分析JDK7”这一栏往下阅读)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值