设计模式之代理模式

设计模式之代理模式以及动态代理的原理

请添加图片描述

现实世界

在讲述之前先描述我们的一个生活场景-中介

请求服务有两种方式:

  • 直接和服务者交互
  • 中介

直接和服务者交互

在这里插入图片描述

利用中介

在这里插入图片描述

现实世界为什么需要中介呢,中介有更多的资源,其责任只是介绍,并不是真正的业务交互者。在现实世界中中介也存在缺点,中间商赚差价。

在代码世界,上述关系能更好的分离需求者和服务者,使职责更加清晰。一个中间者可代理多个类,拓展性比较好。

那么在代码世界如何实现上述关系呢。

代码世界

代理模式类图:

在这里插入图片描述

对上述图进行解析:

Interface:抽象接口。

Client:服务调用者,直接调用ProxyClassProxyClass会调用RealClass

ProxyClass:中介,实现Interface

RealClass:真实服务提供者,实现Interface

下面用代码实现此关系

实现功能:用户去按摩店按摩

静态代理

静态代理则是程序员手动编写的代码.

行为,抽象接口类

按摩接口

public interface Massage {
    void massage();
}

中介按摩店类

实现Massage,内部声明一个需代理的对象,并在真正服务前后特殊处理

public class Parlor implements Massage{
    Massage massage;
    Parlor(Massage massage) {
        this.massage = massage;
    }

    @Override
    public void massage() {
        //服务前
        System.out.println("正帮助您安排技师。。。");
        massage.massage();
        //服务后
        System.out.println("服务结束");
    }
}

真实服务类小红技师

实现代理类,并提供服务

public class XiaoHong implements Massage {

    @Override
    public void massage() {
        System.out.println("XiaoHong 为您服务");
        System.out.println("服务中。。。");
        System.out.println("服务中。。。");
        System.out.println("XiaoHong 服务结束");
    }
}

客户类Customer

客人,去按摩店,点小红技师进行服务

public class Customer {
    public static void main(String[] args) {
        Massage massage = new Parlor(new XiaoHong());
        massage.massage();
    }
}

运行结果

正帮助您安排技师。。。
XiaoHong 为您服务
。。。
。。。
XiaoHong 服务结束
服务结束

可以看到整个过程都是程序员手动编写,此实现则为静态代理,可在服务前后进行特殊操作处理(切面)。
此种实现存在一个问题,如果说需要代理的功能增多,那么程序员需要不断修改代理类,不便于后期维护。

动态代理

静态代理是程序员自己维护代理类,而动态代理是把代理类交给jdk去维护,jdk自动生成代理类。

代码改进

如果上述代码改为动态代理实现则如下:

public class Customer {
    public static void main(String[] args) {
        Massage massage = (Massage) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Massage.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("正帮助您安排技师。。。");
                method.invoke(new XiaoHong(), null);
                System.out.println("服务结束");
                return null;
            }
        });
        massage.massage();
        //打印结果:
//        正帮助您安排技师。。。
//        XiaoHong 为您服务
//        。。。
//        。。。
//        XiaoHong 服务结束
//        服务结束
    }
}

代理对象是通过ProxynewProxyInstance来生成的,有对象即一定有类,对象一定是通过类创建的。

原理解析

源码为jdk11

Proxy#newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
    Objects.requireNonNull(h);
    Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass();
  	//自动生成代理类,并获取代理类的构造
    Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
    return newProxyInstance(caller, cons, h);
}

Proxy#getProxyConstructor

private static Constructor<?> getProxyConstructor(Class<?> caller, ClassLoader loader, Class<?>... interfaces) {
	...

    //对类的构造进行缓存处理
    return (Constructor)proxyCache.sub(intf).computeIfAbsent(loader, (ld, clv) -> {
        //获取类,并获取类的构造进行返回
        return (new Proxy.ProxyBuilder(ld, (Class)clv.key())).build();
    });
    
    ...

}

本篇文章不分析缓存,只考虑代理类和代理对象是怎么生成的

ProxyBuilder#build

Constructor<?> build() {
    //获取代理类
    Class proxyClass = defineProxyClass(this.module, this.interfaces);
	...
    //构造
    final Constructor cons;
    //获取构造,Proxy.constructorParams = new Class[]{InvocationHandler.class};构造的参数为InvocationHandler类
    cons = proxyClass.getConstructor(Proxy.constructorParams);
	...
    //设置安全性
    cons.setAccessible(true);
    
    return cons;
}

ProxyBuilder#defineProxyClass

private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
    
    ...
	
    //生成class文件的字节数组
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, (Class[])interfaces.toArray(Proxy.EMPTY_CLASS_ARRAY), accessFlags);
    
    ...

    //获取Class,native方法不再分析
    Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile, 0, proxyClassFile.length, loader, (ProtectionDomain)null);
	...
    return pc;
}

jdk通过上述方法生成代理类。我们可以将byte数组输出,查看生成的文件。

类似上面方法,我们借助generateProxyClass方法输出byte数组

private static void creatProxy() throws Exception {
    String name = "Proxy0.class";
    //生成代理指定接口的Class数据
    File file = new File("D:\\" + name);
    file.createNewFile();
    byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Massage.class});
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(bytes);
    fos.close();
}

系统生成的类,为方便观察,在Massage 接口类加入了washFoot方法。

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

    //构造,生成的对象调用了此构造
    public Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            //执行方法时,会把参数,当前方法,当前对象传给InvocationHandler
            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 massage() 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 washFoot() throws  {
        try {
            super.h.invoke(this, m4, (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);
        }
    }
    //上述所有方法都调用构造传入的参数的invoke

    //当类被加载时则调用下面代码块,拿到对象的默认方法和接口要实现的方法
    static {
        try {
            //默认方法
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            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.hbsd.demo1.Massage").getMethod("massage");
            m4 = Class.forName("com.hbsd.demo1.Massage").getMethod("washFoot");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

当通过上述类生成的对象调用方法时,默认调用构造传入InvocationHandler参数的invoke方法。

InvocationHandler参数是之前newProxyInstance的第三个参数,invoke则也是执行的此参数的invoke,返回Proxy#newProxyInstance的最后一行return newProxyInstance(caller, cons, h);

Proxy#newProxyInstance

private static Object newProxyInstance(Class<?> caller, Constructor<?> cons, InvocationHandler h) {
    //h为传入的InvocationHandler,cons是获取的只需要一个参数InvocationHandler的构造
	...
    return cons.newInstance(h);
    ...

}

InvocationHandler.invoke

InvocationHandler是之前调用newProxyInstance时,new的匿名内部类

在回过头分析此匿名内部类的invoke方法

//proxy,当前的代理对象,在invoke里调用此对象的方法都将导致栈溢出,此对象的所有方法都会再次执行invoke。
//method,当前调用的方法
//当前调用方法传入的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("正帮助您安排技师。。。");
    //方法调用前 切面
    method.invoke(new XiaoHong(), null);
    //方法调用后 切面
    System.out.println("服务结束");
    return null;
}

可在方法调用前后添加切面

总结

代理模式

  • 隔离用户和真正服务者,职责隔离。
  • 面向切面编程,可以在方法执行前和执行后加入特殊的处理。

静态代理

程序员自己编写,随着后续代理功能的增多,需要不断对代理类进行维护。

动态代理

由jdk自动退生成的类,由jdk帮助程序员维护,降低成本。

原 创 不 易 , 还 希 望 各 位 大 佬 支 持 一 下 \textcolor{blue}{原创不易,还希望各位大佬支持一下}

👍 点 赞 , 你 的 认 可 是 我 创 作 的 动 力 ! \textcolor{green}{点赞,你的认可是我创作的动力!}

⭐️ 收 藏 , 你 的 青 睐 是 我 努 力 的 方 向 ! \textcolor{green}{收藏,你的青睐是我努力的方向!}

✏️ 评 论 , 你 的 意 见 是 我 进 步 的 财 富 ! \textcolor{green}{评论,你的意见是我进步的财富!}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值