硬肝系列:23种设计模式之代理模式

用示例代码来帮你了解代理模式

对于“设计模式”这个词大家肯定都不陌生,很多框架也用到了设计模式,但是大部分的开发者应该是没有深入的了解过,我准备硬肝下这23设计模式作为专题文章的开端,一共23种设计模式,我尽量在<23天肝完。

为什么要学习设计模式:https://blog.csdn.net/kaituozhe_sh/article/details/107922339

在我大学四年,对设计模式也没有什么概念,写代码就想着能实现就可以了,不会有设计模式那样的思想,但是当学习到了框架的时候,对于设计模式才有了一些更深入的了解,使用设计模式的代码在扩展性上会比暴力的代码更容易维护,特别是当一个程序猿离职了后,你去接手它的代码,里面是一大堆if else,这样真的会崩溃,修改都不知道从何下手
在这里插入图片描述

硬肝系列目录

创建型模式

23种设计模式之工厂模式

23种设计模式之抽象工厂模式

23种设计模式之建造者模式

23种设计模式之原型模式

23种设计模式之单例模式

结构型模式

23种设计模式之适配器模式

23种设计模式之桥梁模式

23种设计模式之代理模式

23种设计模式之外观模式

23种设计模式之装饰器模式

23种设计模式之享元模式

23种设计模式之组合模式

行为型模式

23种设计模式之责任链模式

23种设计模式之命令模式

23种设计模式之迭代器模式

23种设计模式之中介者模式

23种设计模式之备忘录模式

23种设计模式之观察者模式

到目前为止、23种设计模式的创建型模式已经给大家肝完了,现在我们进入到一个全新的章节,结构型模式!!!

什么是代理模式

代理代理,我们从字面上就能看得出来,叫别人帮你你干活,干什么活呢?我来举个例子吧,比如你是一个炒鸡大明星,都是炒鸡的明星了,肯定有很多活动来找你吧,这你就需要一个或者多个助理来帮你处理了,你只负责参加活动,其他的什么都不用你管,表演前的签合同、表演后的善后工作都由小助理帮你处理,这样你才有时间参加更多的活动吧
在这里插入图片描述
然而代理呢也分为三种
1.静态代理
2.JDK的动态代理
3.CGlib的动态代理
下面我分别用实例来给你们讲讲它们三个的优缺点

静态代理

现在我们先新建一个Star接口,这里面的方法为明星们需要做的事

public interface Star {
    void say();
    void play();
}

接下来用一个SuperStar,实现Star接口

public class SuperStart implements Star{
    @Override
    public void say() {
        System.out.println("我是超级大明星,我准备表演节目!!");
    }

    @Override
    public void play() {
        System.out.println("我是超级大明星,我正在表演节目!!");
    }
}

创建一个静态代理类来实现代理的效果

public class StarStaticProxy {
    private Star star;
    public StarStaticProxy(Star star){
        this.star = star;
    }
    public void proxySay(){
        System.out.println("我是ta的助理,我帮ta接工作,签合同");
        star.say();
        System.out.println("我是ta的助理,表演完了我帮ta做善后工作");
    }
    public void proxyPlay(){
        System.out.println("我是ta的助理,我帮ta准备演出服装");
        star.play();
        System.out.println("我是ta的助理,表演完了我帮ta回收演出服装");
    }
}

从上面的代码我们可以看出,就相当于我只需要关注自己做的事,其他的事有别人搞定,下面我们来测试一下

public static void main(String[] args) {
        StarStaticProxy starStaticProxy = new StarStaticProxy(new SuperStart());
        starStaticProxy.proxySay();
        System.out.println();
        starStaticProxy.proxyPlay();
    }

输出结果:

我是ta的助理,我帮ta接工作,签合同
我是超级大明星,我准备表演节目!!
我是ta的助理,表演完了我帮ta做善后工作

我是ta的助理,我帮ta准备演出服装
我是超级大明星,我正在表演节目!!
我是ta的助理,表演完了我帮ta回收演出服装

这样就完成了最简单的代理实现方式,这样写是挺方便的,但是我要是细化到每个人呢,我又100个明星,难道我要写100个静态代理类吗??
在这里插入图片描述
显然是不可能的,那我们应该用什么方法来解决呢?这时候就出现了动态代理了,我们Spring AOP使用的就是动态代理技术,对于提供了接口的类,我们使用的是JDK的动态代理,没有提供接口的类,我们选择的是CGlib动态代理,我们先来说说JDK动态代理,为什么它只能对接口进行代理

JDK动态代理

还是上面明星的例子,我们直接建立动态代理类

public class StarDynamicProxy {
    private Object object;
    public StarDynamicProxy(Object object){
        this.object = object;
    }
    public Object getDynamicProxy(){
        return Proxy.newProxyInstance(
                object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("我是明星的智能助理,开启ta一天的事务");
                        Object o = method.invoke(object,args);
                        System.out.println("我是明星的智能助理,总结ta一天的事务");
                        return o;
                    }
                }
        );
    }
}

我们先来测试一下,能不能达到我们理想的效果

public static void main(String[] args) {
        SuperStart superStart = new SuperStart();
        Star star = (Star) new StarDynamicProxy(new SuperStart()).getDynamicProxy();
        star.say();
    }

运行结果

我是明星的智能助理,开启ta一天的事务
我是超级大明星,我准备表演节目!!
我是明星的智能助理,总结ta一天的事务

在这里插入图片描述
好了,接下来我们看看JDK动态代理为什么只能代理接口吧,以及那些参数的意义

Proxy.newProxyInstance(
                object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),
                //调用处理器
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("我是明星的智能助理,开启ta一天的事务");
                        Object o = method.invoke(object,args);
                        System.out.println("我是明星的智能助理,总结ta一天的事务");
                        return o;
                    }
                }
        );

我说这上面这些代码是最重要的应该没人反对吧,我们先来看看newProxyInstance()的源码

@CallerSensitive
	//ClassLoader loader为类加载器 object.getClass().getClassLoader()即可获得
	//Class<?>[] interfaces为被代理类实现的接口,为什么是一个数组,因为接口可以多实现
	//h为InvocationHandler的实现类,上面我们直接new了一个InvocationHandler,也可以写一个实现类来实现此接口
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

我给大家截取了比较重要的几段,一步一步来
在这里插入图片描述
进入到getProxyClass0方法中

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
        	//限制被代理接口的数量
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //上面这段话的意思是
        //JDK对被代理类进行缓存,如果缓存中存在了这个被代理类则直接返回,反之则会通过ProxyClassFactory来进行创建
        return proxyClassCache.get(loader, interfaces);
    }

proxyClassCache是通过WeakCache实现的,WeakCache<K,P,V>中,K代表key值,P代表参数,V代表存储的值。此类用于缓存{(key,sub-key)–>value}kv对,具体的缓存实现我们可以另开一篇文章来讲,我们主要关心动态代理类如何生成的

上面说到了ProxyClassFactory,它是Proxy里的一个静态内部类,实现了BiFunction接口并重写了apply方法

源码

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 所有代理类名的前缀
        private static final String proxyClassNamePrefix = "$Proxy";

        // 用于生成唯一代理类名的计数器
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //intf为被代理类的接口,前面newProxyInstance已将它放入
            for (Class<?> intf : interfaces) {
                /*
                 *验证类加载器是否将此接口的名称解析为相同的类对象。
                 */
                Class<?> interfaceClass = null;
                try {
                	//获取实现接口的类
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                //下面为验证接口实现类的代码
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // 生成代理类的包名
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            //记录非公共代理接口的包,以便代理类将在同一个包中定义。验证所有非公共代理接口都在同一个包中。
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * 生成指定的代理类
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
            	//根据二进制字节码返回对应的Class实例
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

关键代码
在这里插入图片描述
我们来看看ProxyGenerator.generateProxyClass方法
在这里插入图片描述
到这,我们就可以获得通过JDK动态代理而得到的代理类了,为什么只能代理接口呢,因为被代理类是继承了Proxy类,要知道类不能多继承,而接口可以都多实现,所以它只能实现接口的动态代理,但是我有些类确确实实不需要实现接口,但是我又得实现代理该怎么办呢?这时候CGlib动态代理就很好的弥补了这个问题

CGlib动态代理

CGlib(Code Generation Library)是一个Code生成类库,它可以在运行期扩展Java类与实现Java接口。在Spring AOP上也有它的身影,CGlib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

我们先上代码来体验一下CGlib的强大之处

新建一个不实现接口的类

public class Superman {
    public void say(){
        System.out.println("i'm superman");
    }
}

CGlib代理类

public class StarCGlibProxy implements MethodInterceptor {
    private Object cglib;
    public StarCGlibProxy(Object cp){
        cglib = cp;
    }
    public Object getProxyInstance(){
        //1.Enhancer工具类
        Enhancer enhancer = new Enhancer();
        //2.设置被代理类为父类
        enhancer.setSuperclass(cglib.getClass());
        //3.设置回调函数
        enhancer.setCallback(this);
        //4.创建子类(也就是被代理的对象)
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("穿上红内裤---");
        Object returnReflect = method.invoke(cglib,objects);
        System.out.println("不穿红内裤---");
        return returnReflect;
    }
}

我们来测试一下,是否是我们想要的结果

public static void main(String[] args) {
        Superman superman = (Superman) new StarCGlibProxy(new Superman()).getProxyInstance();
        superman.say();
    }

输出结果

穿上红内裤---
i'm superman
不穿红内裤---

我们来看看代理类中有哪些内容,首先代理类实现了MethodInterceptor接口并重写了intercept()
在这里插入图片描述
这个接口中就包含了一个方法,该方法有4个参数:

1)var1 表示要增强的对象,即实现这个接口类的一个对象;

2)var2 表示要被拦截的方法;

3)var3 表示要被拦截方法的参数;

4)var4 表示要触发父类的方法对象;

还有就是大家注意看

public Object getProxyInstance(){
        //1.Enhancer工具类
        Enhancer enhancer = new Enhancer();
        //2.设置被代理类为父类
        enhancer.setSuperclass(cglib.getClass());
        //3.设置回调函数
        enhancer.setCallback(this);
        //4.创建子类(也就是被代理的对象)
        return enhancer.create();
    }

我通过Enhancer来创建代理对象

首先设置被代理类为父类,也就是Superman
在这里插入图片描述
继承了父类且实现了Factory接口

其次设置回调函数为MethodInterceptor接口所实现的Callback函数

最后我调用了enhancer.create();其方法调用栈为:
enhancer.create()—>createrHelper()—>AbstractClassGenerator.create(key)来创建代理对象,我们先来看看这个create方法到底是啥

    public Object create() {
        this.classOnly = false;
        this.argumentTypes = null;
        return this.createHelper();
    }

点击进去只有这么一块,最主要是createHelper()

    private Object createHelper() {
        this.preValidate();
        Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }

首先**this.preValidate()**进行有效性验证比如有多个callBack却没有callBackFilter

其次再根据KEY_FACTORY 凭当前代理类的配置信息 生成一个组合Key,再利用这个组合Key,调用super.create(key);

再看看父类的create源码

protected Object create(Object key) {
        try {
            ClassLoader loader = this.getClassLoader();
            Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
            AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
            if (data == null) {
                Class var5 = AbstractClassGenerator.class;
                synchronized(AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);
                        data = new AbstractClassGenerator.ClassLoaderData(loader);
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }
            this.key = key;
            Object obj = data.get(this, this.getUseCache());
            return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
        } catch (Error | RuntimeException var9) {
            throw var9;
        } catch (Exception var10) {
            throw new CodeGenerationException(var10);
        }
    }

父类方法真正返回代理类对象的是this.nextInstance(obj)方法,该方法在父类为抽象方法,在子类中实现
在这里插入图片描述

protected Object nextInstance(Object instance) {
        Enhancer.EnhancerFactoryData data = (Enhancer.EnhancerFactoryData)instance;
        if (this.classOnly) {
            return data.generatedClass;
        } else {
            Class[] argumentTypes = this.argumentTypes;
            Object[] arguments = this.arguments;
            if (argumentTypes == null) {
                argumentTypes = Constants.EMPTY_CLASS_ARRAY;
                arguments = null;
            }

            return data.newInstance(argumentTypes, arguments, this.callbacks);
        }
    }

我们接着来看最后一行data.newInstance(argumentTypes, arguments, this.callbacks);

//argumentTypes为代理对象的构成器类型,arguments为代理对象构造方法参数,callbacks为对应回调对象。
public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
            this.setThreadCallbacks(callbacks);

            Object var4;
            try {
                if (this.primaryConstructorArgTypes == argumentTypes || Arrays.equals(this.primaryConstructorArgTypes, argumentTypes)) {
                    var4 = ReflectUtils.newInstance(this.primaryConstructor, arguments);
                    return var4;
                }

                var4 = ReflectUtils.newInstance(this.generatedClass, argumentTypes, arguments);
            } finally {
                this.setThreadCallbacks((Callback[])null);
            }

            return var4;
        }

通过传进来的参数,使用反射工具类来创建代理类对象

我们来看看cglib生成的代理对象
在这里插入图片描述
如上图所示
我们来看看反编译后的代码
在这里插入图片描述
太长我就不全部贴出来了,我截了比较重要的一段
在这里插入图片描述
从这段代码我们可以看出,当我们调用被代理对象的say方法时,会执行intercept方法进行拦截,以达到增强的目的,最后一个tips:

invokeSuper调用的是被代理类的方法, 但只有代理类才存在基类, 必须使用代理类作为obj参数调用

invoke调用的是增强方法, 必须使用被代理类的对象调用, 使用代理类会造成OOM

每次看完源码都感觉身体被掏空
在这里插入图片描述
在这里插入图片描述
犒劳一下自己

呜呜呜,生活还要继续,加油干!!!

完成:TO: 2021/3/22 00:00

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的海滨体育馆管理系统,源码+数据库+毕业论文+视频演示 本基于Spring Boot的海滨体育馆管理系统设计目标是实现海滨体育馆的信息化管理,提高管理效率,使得海滨体育馆管理工作规范化、高效化。 本文重点阐述了海滨体育馆管理系统的开发过程,以实际运用为开发背景,基于Spring Boot框架,运用了Java技术和MySQL作为系统数据库进行开发,充分保证系统的安全性和稳定性。本系统界面良好,操作简单方便,通过系统概述、系统分析、系统设计、数据库设计、系统测试这几个部分,详细的说明了系统的开发过程,最后并对整个开发过程进行了总结,实现了海滨体育馆相关信息管理的重要功能。 本系统的使用使管理人员从繁重的工作中解脱出来,实现无纸化办公,能够有效的提高海滨体育馆管理效率。 关键词:海滨体育馆管理,Java技术,MySQL数据库,Spring Boot框架 本基于Spring Boot的海滨体育馆管理系统主要实现了管理员功能模块和学生功能模块两大部分,这两大功能模块分别实现的功能如下: (1)管理员功能模块 管理员登录后可对系统进行全面管理操作,包括个人中心、学生管理、器材管理、器材借出管理、器材归还管理、器材分类管理、校队签到管理、进入登记管理、离开登记管理、活动预约管理、灯光保修管理、体育论坛以及系统管理。 (2)学生功能模块 学生在系统前台可查看系统信息,包括首页、器材、体育论坛以及体育资讯等,没有账号的学生可进行注册操作,注册登录后主要功能模块包括个人中心、器材管理、器材借出管理、器材归还管理、校队签到管理、进入登记管理、离开登记管理、活动预约管理。
毕业设计,基于SpringBoot+Vue+MySQL开发的学科竞赛管理系统,源码+数据库+毕业论文+视频演示 随着国家教育体制的改革,全国各地举办的竞赛活动数目也是逐年增加,面对如此大的数目的竞赛信息,传统竞赛管理方式已经无法满足需求,为了提高效率,竞赛管理系统应运而生。 本学科竞赛管理系统以实际运用为开发背景,基于Spring Boot框架、Vue框架,运用了Java语言和MySQL数据库进行开发设计,充分保证系统的安全性和稳定性。本系统界面良好,操作简单方便,通过系统概述、系统分析、系统设计、数据库设计、系统测试这几个部分,详细的说明了系统的开发过程,最后并对整个开发过程进行了总结,实现了学科竞赛管理的重要功能。 本学科竞赛管理系统运行效果稳定,操作方便、快捷,界面友好,是一个功能全面、实用性好、安全性高,并具有良好的可扩展性、可维护性的学科竞赛管理平台。 关键词:学科竞赛管理,Java语言,MySQL数据库,Vue框架 此学科竞赛管理系统的设计与实现功能分析主要分为管理员功能模块和学生功能模块两大模块,下面详细介绍这两大模块的主要功能: (1)管理员:管理员登陆后可对系统进行全面管理,管理员主要功能模块包括个人中心、学生管理、教师管理、赛项信息管理、赛项报名管理、奖项统计管理、管理员管理以及系统管理,管理员实现了对系统信息的查看、添加、修改和删除的功能。 (2)学生:学生进入本学科竞赛管理系统的设计与实现前台可查看系统信息,包括首页、赛项信息以及新闻资讯等,注册登录后主要功能模块包括个人中心和赛项报名管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉淀顶峰相见的PET

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值