设计模式:01-代理模式

一、使用代理模式的场景:对已经开发好的类进行功能增强(添加或修改),比如有一个接口:

package com.proxy;

public interface Animal {
    void dog();
    void cat();
    void monkey();
}

以及该接口的一个实现类:

package com.proxy;

public class Dog implements Animal {
    @Override
    public void dog() {
        System.out.println("I am a dog");
    }

    @Override
    public void cat() {
        System.out.println("I am a dog, I like cat!");
    }

    @Override
    public void monkey() {
        System.out.println("I am a dog, I dont not like monkey!");
    }
}

现在要对Dog这个类进行增强,在调用dog、cat和monkey方法前后打印一些日志。可以创建Dog的一个代理类(DogProxy),该代理类持有一个Dog对象。

package com.proxy;

public class DogProxy implements Animal {

    private final Dog dog;

    public DogProxy(Dog dog) {
        this.dog = dog;
    }

    @Override
    public void dog() {
        System.out.println("---------------before dog");
        dog.dog();
        System.out.println("---------------after dog");
    }

    @Override
    public void cat() {
        System.out.println("---------------before cat");
        dog.cat();
        System.out.println("---------------after cat");
    }

    @Override
    public void monkey() {
        System.out.println("---------------before monkey");
        dog.monkey();
        System.out.println("---------------after monkey");
    }
}

可以写一个主类,调用DogProxy这个代理类的方法。

package com.proxy;

public class ProxyTest {
    public static void main(String[] args) {
        DogProxy dogProxy = new DogProxy(new Dog());
        dogProxy.dog();
        dogProxy.cat();
        dogProxy.monkey();
    }
}

运行程序输出如下:
在这里插入图片描述
上述方法称为静态代理,代理类中的所有代理方法都是由程序员手动写代码完成。

二、为什么要用动态代理?
假设有一个Animal接口的实现类Monkey,代码如下:

package com.proxy;

public class Monkey implements Animal {

    @Override
    public void dog() {
        System.out.println("I am a monkey, I dont not like dog!");
    }

    @Override
    public void cat() {
        System.out.println("I am a cat, I like dog!");
    }

    @Override
    public void monkey() {
        System.out.println("I am a monkey, I like cat!");
    }
}

现在有一个需求,需要在调用Monkey的dog()方法、cat()方法和monkey()方法前后都要打印系统当前时间。可以模仿上一小节中的方法来完成MonkeyProxy。但是有一个问题:假如Animal()接口有10000个方法,而Monkey类实现了Animal接口中的10000个方法,要按照静态代理的方法来写MonkeyProxy类的话,就需要在该类中写10000个代理方法,显然是不合理的。合理的方法是使用jdk 动态代理
使用jdk动态代理的MonkeyProxy类示例如下:

package com.proxy;

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

public class MonkeyProxy  implements Animal {

    private Monkey monkey = new Monkey();

    Animal animal = (Animal) Proxy.newProxyInstance(Monkey.class.getClassLoader(), new Class<?>[]{Animal.class}, new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("-----------------" + new Date().toLocaleString());
            method.invoke(monkey, args);
            System.out.println("-----------------" + new Date().toLocaleString());
            return null;
        }
    });

    @Override
    public void dog() {
        animal.dog();
    }

    @Override
    public void cat() {
        animal.cat();
    }

    @Override
    public void monkey() {
        animal.monkey();
    }
}

该代理类持有一个Monkey类的对象: private Monkey monkey = new Monkey();
同时通过Proxy类(jdk的一个类)生成了一个动态类对象:Animal animal= (Animal) Proxy.newProxyInstance(。。。),该代理类的方法dog()、cat()和monkey()则分别调用这个动态类对象的方法:

animal.dog();
animal.cat();
animal.monkey();

写一个测试类,代码如下:

package com.proxy;

public class ProxyTest {
    public static void main(String[] args) {
        //  旧版jdk版本,在本地生成动态代理类,否则只存在与内存
//        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        //  新版jdk版本
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
        MonkeyProxy monkeyProxy = new MonkeyProxy();
        monkeyProxy.cat();
        monkeyProxy.dog();
        monkeyProxy.monkey();
    }
}

运行结果如下:
在这里插入图片描述
从运行结果可以看出,调用MonkeyProxy类对象的dog()、cat()和monkey()方法时,都调用了该类中定义的动态类对象animal构建时所传入的InvocationHandler中的invoke()方法。这是怎么发生的呢?需要看jdk动态生成的类的源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.proxy.Animal;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

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

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void cat() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void monkey() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void dog() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.proxy.Animal").getMethod("cat");
            m3 = Class.forName("com.proxy.Animal").getMethod("monkey");
            m5 = Class.forName("com.proxy.Animal").getMethod("dog");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

该类对象通过构造方法,传入InvocationHandler类对象:

public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

该动态类的父类为Proxy,Proxy类的构造方法如下:

protected Proxy(InvocationHandler h) {
     Objects.requireNonNull(h);
     this.h = h;
}

而h定义如下:

protected InvocationHandler h;

总结一下就是,Proxy0这个动态类对象在构造时会把传入的InvocationHander对象保存到自己的protected类型的变量h中。
再看动态类里的monkey()方法:

public final void monkey() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

核心的一句是:

super.h.invoke(this, m3, (Object[])null);

其中super.h为刚才提到的h。那么m3呢?来看静态代码块里的定义:

static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.proxy.Animal").getMethod("cat");
            m3 = Class.forName("com.proxy.Animal").getMethod("monkey");
            m5 = Class.forName("com.proxy.Animal").getMethod("dog");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

也就是主类中调用eat方法时,最终会调用jdk生成的动态对象的invoke()方法。

invoke(this, Class.forName("com.proxy.Animal").getMethod("monkey");, (Object[])null);

而invoke方法的定义上边已经贴过:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println("-----------------" + new Date().toLocaleString());
      method.invoke(monkey, args);
      System.out.println("-----------------" + new Date().toLocaleString());
      return null;
}

代码比较简单,就是反射,不再详述。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值