zoukankan      html  css  js  c++  java
  • 深入理解JAVA中的代理模式

    前言

    代理是什么

    事故现场:我家的宠物今天生病了,而我又没有相关的医学知识,所以我只让我的宠物多喝热水吗?

    结果显然是不行的,所以我需要去找宠物医生这些更专业的人来帮我的宠物治病。

    这个时候,代理就出现了,而宠物医生就是代理,而我就是目标对象。

    总结起来就是代理代替目标对象执行相关操作,即是对目标对象的一种功能扩展。

    使用代理模式的条件

    1、两个角色:执行者,被代理对象

    2、注重过程,必须要做,被代理的对象没时间做或者不想做,不专业

    3、执行者必须拿到被代理对象的个人资料

    1.静态代理

    代码实现:

    /**
     * 我和宠物医生都是人,都有治疗技能,但是宠物医生比我更专业
     */
    interface IPerson{
        void treat(Pet pet); //治疗技能
    }
    /**
     * 宠物类
     */
    class Pet{
        private String name;
        public Pet(String name){
            this.name = name;
        }
        public String getName() {
            return name;
        }
    }
    
    /**
     * 目标对象实现”IPerson“接口
     */
    class Self implements IPerson{
        private Pet pet;
        public Self(Pet pet){
            this.pet = pet;
        }
        public void treat(Pet pet){
            System.out.println(pet.getName() + ",你要多喝点水");
        }
    }
    
    /**
     * 代理对象与目标对象实现同一接口
     */
    class PetDoctor implements IPerson{
        //接收目标对象
        private IPerson targetObj;
        public PetDoctor(IPerson targetObj){
            this.targetObj = targetObj;
        }
        @Override
        public void treat(Pet pet) {
            System.out.println("对" + pet.getName() + "进行检查");
            targetObj.treat(pet);
            System.out.println("对" + pet.getName() + "进行治疗");
        }
    }

    代码测试:

    public static void main(String[] args){
            //我的宠物
            Pet pet = new Pet("多多");
            //目标对象
            IPerson target = new Self(pet);
            //代理对象
            IPerson proxy = new PetDoctor(target);
            proxy.treat(pet);
        }

    运行结果:

    宠物医生对多多进行检查
    我对多多说,你要多喝点水
    宠物医生对多多进行治疗

    结果很明显,医生比我更专业,我只会让我的宠物喝水,但医生会先检查再进行专业的治疗,所以说代理是让更专业的对象帮你做事。

    2.动态代理

    动态代理又分为jdk动态代理和cglib动态代理,两者的区别是jdk动态代理的实现是基于接口,而cglib动态代理是基于继承,但两者做的是同一件事,那就是字节码重组

    基本流程都是根据目标对象的资料,通过反射获取该对象的信息,然后根据信息按照特定的写法重写一个java类,再进行编译并动态加载到JVM中运行,所以说动态代理在底层其实就是实现了字节码重组。

     jdk动态代理实例演示

     Person接口

    //定义Person接口,技能是煮饭
    public interface Person {
        void cook();
    }

    我自己,也就是被代理的对象,但我只会做可乐鸡翅

    public class Oneself implements Person {
        @Override
        public void cook() {
            System.out.println("我会做可乐鸡翅");
        }
    }

    动态代理类,也是一个厨师,因为初始对于做菜比我更专业

    public class Kitchener implements InvocationHandler{
        //需要代理的目标对象
        private Object object;
    
        public Kitchener(Object object){
            this.object = object;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("我会做糖醋排骨");
            method.invoke(object,args);    //这是我会做的,其余两样是代理对象初始会做的
            System.out.println("我会做九转大肠");
            return null;
        }
    }

    测试代码

    public class TestJdk {
        public static void main(String[] args){
            //创建目标代理对象
            Oneself oneself = new Oneself();
    
            InvocationHandler kitchener = new Kitchener(oneself);
            /*
             * 通过Proxy的newProxyInstance方法来创建我们的代理对象,做的就是字节码重组的工作,新生成一个java类在编译再加载到JVM运行
             * 第一个参数是类加载器
             * 第二个参数是我们这里为代理对象提供的接口,也就是代理对象所实现的接口,所以说在jdk动态代理中被代理对象需要实现一个接口
             * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
             */
            Person proxy = (Person) Proxy.newProxyInstance(kitchener.getClass().getClassLoader(),
                                                            oneself.getClass().getInterfaces(),
                                                            kitchener);
            System.out.println(proxy.getClass());  // (1)
            proxy.cook();
    
        }
    }

    测试结果

    class com.sun.proxy.$Proxy0    //(2)
    我会做糖醋排骨
    我会做可乐鸡翅
    我会做九转大肠

    可以看到(1)行代码打印出来的是一个代理类,而代理对象通过生成java类再编译加载运行对用户来说是无感知的,我们只知道返回回来的是一个代理对象,然后由代理对象去帮我们做事。

    而cglib代理的实现原理也是一样的,只不过一个是基于接口,一个是基于继承,原理都是通过反射获取对象信息再根据对象信息创建java类编译加载运行,所以cglib暂时就不展开了,后期可以自己手写一个动态加深理解。

    学习了动态代理后,在本人的工作中是没使用过的,但却是了解spring的AOP实现的必要基础,因为spring的AOP实现就是基于动态代理实现的。

  • 相关阅读:
    2021.12.7
    2021.12.13(观察者模式c++)
    2021.12.05(echarts生成mysql表词云)
    2021.12.10(申请加分项)
    2021.12.10(课程总结)
    2021.12.11(Linux,yum错误,There are no enabled repos.)
    12月读书笔记02
    2021.12.12(springboot报ScannerException)
    2021.12.09
    centos国内镜像站
  • 原文地址:https://www.cnblogs.com/xiguadadage/p/10188634.html
Copyright © 2011-2022 走看看