设计模式之代理模式(结构型)

在介绍代理模式之前,博主想说点儿“题外”话,博主喜欢玩游戏,特别喜欢玩英雄联盟,尤其喜欢玩压缩,博主的压缩玩的可以说“出神入化”,博主的朋友都叫博主“托儿索”,话说回来,博主的压缩虽然玩的出神入化,特别喜欢浪,但是补刀不咋滴,作为一个程序员,博主决定统计一下楼主的补刀数。

上代码:

public class YaSuo implements Mendable{

    @Override
    public int mend() {
        int killNum = new Random().nextInt(100);
        return killNum;
    }

    public static void main(String[] args) {
        new YaSuo().mend();
    }
}

interface Mendable {
    int mend();
}

代码很简单,说这个“YaSuo”,有一个补刀的方法,在随缘的补刀。问题来了,我想记录一下这个“YaSuo”补刀的个数,我想大家已经想出来了,不就是加个日志,打印一下补刀的个数嘛,来上代码:

public class YaSuo implements Mendable{

    @Override
    public int mend() {
        System.out.println("YaSuo killing batman...");
        int killNum = new Random().nextInt(100);
        System.out.println("YaSuo 10 min mend:" + killNum);
        return killNum;
    }

    public static void main(String[] args) {
        new YaSuo().mend();
    }
}

interface Mendable {
    int mend();
}

来看下结果,博主的随缘补刀大法:

YaSuo killing batman...
YaSuo 10 min mend:67

及格了,博主的补刀能力大增,话说以前都是10 min 补刀 四五十刀。

那么问题又来了,博主不想修改源码或者根本无法修改源码,那怎么办呢 ?

有人说了可以用继承嘛,来上继承:

public class YaSuo implements Mendable {

    @Override
    public int mend() {
        int killNum = new Random().nextInt(100);
        return killNum;
    }

    public static void main(String[] args) {
        new YaSuo1().mend();
    }
}

class YaSuo1 extends YaSuo {

    @Override
    public int mend() {
        System.out.println("YaSuo killing batman...");
        int killNum = super.mend();
        System.out.println("YaSuo 10 min mend:" + killNum);
        return killNum;
    }
}

interface Mendable {
    int mend();
}

继承虽然可以实现结果,但是继承有缺点,耦合度太高,破坏了封装性,我们应该慎用继承。

那该怎么办呢?回到我们今天的重点:代理模式。

用代理来实现,上代码:

public class YaSuo implements Mendable {

    @Override
    public int mend() {
        int killNum = new Random().nextInt(100);
        return killNum;
    }

    public static void main(String[] args) {
        new YaSuoProxy(new YaSuo()).mend();
    }
}

class YaSuoProxy implements Mendable {

    YaSuo yaSuo;

    public YaSuoProxy(YaSuo yaSuo) {
        this.yaSuo = yaSuo;
    }

    @Override
    public int mend() {
        System.out.println("YaSuo killing batman...");
        int killNum = yaSuo.mend();
        System.out.println("YaSuo 10 min mend:" + killNum);
        return killNum;
    }
}

interface Mendable {
    int mend();
}

这就是静态代理

那么问题又来了,作为一个好的程序应该具备良好 的扩展性,现在我统计补刀数,那我们还想统计杀人数、助攻数,那怎么办呢?

那我们可以再加一个代理类来代理统计助攻数,代码如下:

public class YaSuo implements Mendable {

    @Override
    public int mend() {
        int killNum = new Random().nextInt(100);
        return killNum;
    }

    public static void main(String[] args) {
        new YaSuoProxy(new YaSuo()).mend();
    }
}

class YaSuoProxy implements Mendable {

    YaSuo yaSuo;

    public YaSuoProxy(YaSuo yaSuo) {
        this.yaSuo = yaSuo;
    }

    @Override
    public int mend() {
        System.out.println("YaSuo killing batman...");
        int killNum = yaSuo.mend();
        System.out.println("YaSuo 10 min mend:" + killNum);
        return killNum;
    }
}

class YaSuoAssistProxy implements Mendable {

    Mendable m;

    public YaSuoAssistProxy(Mendable m) {
        this.m = m;
    }

    @Override
    public int mend() {
        System.out.println("YaSuo assist...");
        int killNum = m.mend();
        System.out.println("YaSuo assist:" + killNum);
        return killNum;
    }
}

interface Mendable {
    int mend();
}

那我们现在想灵活组合统计补刀数、助攻数,怎么 做呢?来看代码:

public class YaSuo implements Mendable {

    @Override
    public int mend() {
        int killNum = new Random().nextInt(100);
        return killNum;
    }

    public static void main(String[] args) {
        new YaSuoProxy(new YaSuoAssistProxy(new YaSuo())).mend();
    }
}

class YaSuoProxy implements Mendable {

    Mendable m;

    public YaSuoProxy(Mendable m) {
        this.m = m;
    }

    @Override
    public int mend() {
        System.out.println("YaSuo killing batman...");
        int killNum = m.mend();
        System.out.println("YaSuo 10 min mend:" + killNum);
        return killNum;
    }
}

class YaSuoAssistProxy implements Mendable {

    Mendable m;

    public YaSuoAssistProxy(Mendable m) {
        this.m = m;
    }

    @Override
    public int mend() {
        System.out.println("YaSuo assist...");
        int killNum = m.mend();
        System.out.println("YaSuo assist:" + killNum);
        return killNum;
    }
}

interface Mendable {
    int mend();
}

有没有觉得眼熟呢?不错,就是装饰者模式--Decorator。很像装饰者模式。

作为一个优秀的程序,它不应该只能代理一个方法、一种类型,应该还可以代理任何其他可以代理的类型 Object,该怎么办呢?既然有静态代理,有没有动态代理?回答是肯定的。来看动态代理:

public class YaSuo implements Mendable {

    @Override
    public int mend() {
        int killNum = new Random().nextInt(100);
        return killNum;
    }

    public static void main(String[] args) {
        YaSuo yaSuo = new YaSuo();
        Mendable m = (Mendable)Proxy.newProxyInstance(YaSuo.class.getClassLoader(),
                new Class[]{Mendable.class}, (proxy, method, args1) -> {
                    System.out.println("YaSuo killing batman...");
                    Object o = method.invoke(yaSuo, args1);
                    System.out.println("YaSuo 10 min mend:" + o);
                    return o;
                }
        );
        m.mend();
    }
}

interface Mendable {
    int mend();
}

我们也可以写成这样:

public class YaSuo implements Mendable {

    @Override
    public int mend() {
        int killNum = new Random().nextInt(100);
        return killNum;
    }

    public static void main(String[] args) {
        YaSuo yaSuo = new YaSuo();
        Mendable m = (Mendable)Proxy.newProxyInstance(YaSuo.class.getClassLoader(),
                new Class[]{Mendable.class}, new MyHandler(yaSuo));
        m.mend();
    }
}

class MyHandler implements  InvocationHandler {

    Mendable m;

    public MyHandler(Mendable m) {
        this.m = m;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("YaSuo killing batman...");
        Object o = method.invoke(m,args);
        System.out.println("YaSuo 10 min mend:" + o);
        return o;
    }
}

interface Mendable {
    int mend();
}

可以看到代码量很少,我们上面的代码有自己实现的代理类,如:YaSuoProxy、YaSuoAssistProxy,那动态代理有没有实现代理类呢?有的,在内存里,当然我们也可以生成出来。通过以下方式:

public class ProxyTest {
    public static void main(String[] args) {
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Mendable.class});
        try {
        FileOutputStream fileOutputStream = new FileOutputStream("D://$Proxy0.class");
            fileOutputStream.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

生成到D盘。用反编译工具打开来。

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 Mendable {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    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 int mend() throws  {
        try {
            return (Integer)super.h.invoke(this, m3, (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");
            m3 = Class.forName("com.wangkai.design.structure.proxy.v4.Mendable").getMethod("mend");
            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 传到了构造函数当中,生成了equals方法、toString方法、hashCode方法。当然必须有我们的mend方法。当我们生成的代理对象$Proxy0调用mend方法的时候,会去调用InvocationHandler的invoke方法,在invoke方法调用了我们被代理类的mend方法,所以我们才可以在调用被代理类的mend方法前后加自己的代码,比如统计接口时间,比如添加权限校验等等一系列操作,这就是jdk动态代理的逻辑。

可以看到的是,jdk动态代理  被代理类是需要实现接口的。

看完动态代理,再看一下cglib代理:

cglib(Code Generation Library),是一个强大的,高性能,高质量的Code生成类库。

public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(YaSuo.class);
        enhancer.setCallback(new TimeMethodInterceptor());
        YaSuo yaSuo = (YaSuo)enhancer.create();
        yaSuo.mend();
    }
}

class TimeMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println(o.getClass().getSuperclass().getName());
        System.out.println("YaSuo killing batman...");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("YaSuo 10 min mend:" + result);
        return result;
    }
}

class YaSuo {
    public int mend() {
        int killNum = new Random().nextInt(100);
        return killNum;
    }
}

用cglib代理,需要在pom.xml文件加上

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.12</version>
        </dependency>

CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来直接操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy何BeanShell)也使用ASM生成字节码。

cglib代理是通过一个Enhancer和一个MethodInterceptor来实现对方法的拦截,这里的TimeMethodInterceptor 相当于jdk动态代理的InvocationHandler,被代理类没有实现任何接口,那么我们怎么知道生成的代理对象要生成哪个方法呢?其实我们生成的代理对象是被代理类的子类,也就是intercept方法中的参数o,我们打印一下o的父类:

com.wangkai.design.structure.proxy.v5.YaSuo
YaSuo killing batman...
YaSuo 10 min mend:49

可以看到生成的代理类的父类是YaSuo这个类。那么现在问题来了,既然生成的代理类是被代理类的子类,那么如果被代理类是final的话,还能用cglib代理生成代理类吗?我们试一下:

 可以看到报错了。所以是不能的。

jdk代理和cglib代理都介绍完了,我们来对比一下它们:

1、jdk动态代理是实现了被代理对象的接口,在调用具体方法前调用InvokeHandler来处理。如果被代理对象实现了接口,默认情况下会采用jdk的动态代理实现AOP。

cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。并且cglib不可以代理final修饰的类。

2、jdk动态代理是Java自带的,cglib动态代理是第三方jar包提供的。

3、jdk动态代理实现的逻辑是目标类和代理类都实现同一个接口,目标类和代理类是平级的。而cglib动态代理实现的逻辑是继承目标类,是目标类的子类,目标类和代理类是父子继承关系。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值