• 代理模式详解


    1、代理模式概念

    我很忙,忙的没空理你,那你要找我呢,就先找我的代理人吧,那代理人总要知道被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口,被代理人虽然不想干活,但是代理的人能干活呀。

    生活中的房屋中介

    在这里插入图片描述

    2、代理模式分类

    静态代理:
    ​		【定义】
    ​				在程序运行之前,代理类.class文件就已经被创建
    ​		【实现】
    ​				由程序员创建或特定工具自动生成源代码,在对其编译	
    
    动态代理:
    ​		【定义】
    ​				程序运行时通过反射机制动态创建的,对方法的【增强】,不需要修改源码
    ​		【实现】
    ​				基于接口:JDK动态代理
    				基于子类:CGLib动态代理
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.1 静态代理

    【1】目标

    使用静态代理的方式,完成通过房产中介租房的操作;
    
    • 1

    【2】实现

    生活中的房屋中介抽象:

    在这里插入图片描述

    步骤:
    1、创建项目;
    2、定义接口:HouseAgencyCompany及接口中租房的方法:rentingHouse;
    3、定义房主类:HouseOwner 中介类 HouseProxy均实现接口HouseAgencyCompany;
    4、租客类Customer调用HouseProxy完成租房
    
    • 1
    • 2
    • 3
    • 4
    • 5

    【2.2】定义HouseAgencyCompany

    package com.test.spring;
    
    /**
     * @Description:中介公司
     */
    public interface HouseAgencyCompany {
    
        /**
         * @Description 租房子
         */
        void rentingHouse();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    【2.3】定义HouseOwner、HouseProxy

    package com.test.spring;
    
    /**
     * @Description:房东
     */
    public class HouseOwner implements HouseAgencyCompany {
    
        @Override
        public void rentingHouse() {
            System.out.println("房东:签合同");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    package com.test.spring;
    
    /**
     * @Description:中介
     */
    public class HouseProxy implements  HouseAgencyCompany{
    
        private HouseOwner houseOwner;
    
        public HouseProxy() {
            this.houseOwner = new HouseOwner();
        }
    
        @Override
        public void rentingHouse() {
            System.out.println("中介:我带你去看房子");
            System.out.println("中介:这个房子价格很便宜");
            houseOwner.rentingHouse();
            System.out.println("中介:欢迎下次光临");
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    【2.4】定义Customer

    package com.test.spring;
    
    import org.junit.Test;
    
    /**
     * @Description:租客
     */
    public class Customer {
    
        @Test
        public void  needHouse(){
            HouseOwner houseOwner = new HouseOwner();
            houseOwner.rentingHouse();
            System.out.println("=============================");
            HouseAgencyCompany houseAgencyCompany = new HouseProxy();
            houseAgencyCompany.rentingHouse();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    【2.5】运行结果
    在这里插入图片描述

    2.2 动态代理概述

    ​ 代理类在程序运行时创建的方式被成为动态代理。也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的动态生成的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。

    2.2.1 jdk动态代理

    2.2.1.1 jdk动态代理:

    ​ 被代理的类必须基于接口

    java.lang.reflect.Proxy:
    Java动态代理机制的主类,提供了一组静态方法来为一组接口动态地生成代理类及其实例。

    //方法1: 该方法用于获取指定动态代理对象所关联的调用处理器
    static InvocationHandler getInvocationHandler(Object proxy)
     
    //方法2:该方法用于获取关联于指定类装载器和一组接口的动态代理对象
    static Class getProxyClass(ClassLoader loader, Class[] interfaces)
     
    //方法3:该方法用于判断指定类对象是否是一个动态代理类
    static boolean isProxyClass(Class cl)
     
    //方法4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理对象:1、类加载器 2、接口数组、调用处理器(增强部分的业务代码)
    static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    java.lang.reflect.InvocationHandler:
    调用处理器接口,它自定义了一个invoke方法,用于集中处理在动态代理对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理对象时都需要指定一个实现了该接口的调用处理器对象。

    InvocationHandler的核心方法:

    //该方法负责集中处理动态代理类上的所有方法调用。
    //第一个参数是代理对象,第二个参数是被调用的方法对象,第三个方法是调用参数。
    //调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行。
    Object invoke(Object proxy, Method method, Object[] args)
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    2.2.1.2 实现

    2.2.1.2.1 代理接口
    /**
     * @Description:中介公司
     */
    public interface HouseAgencyCompany {
    
        /**
         * @Description 租房子
         */
        void rentingHouse();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    2.2.1.2.2 房东类实现代理接口
    /**
     * @Description:房东
     */
    public class HouseOwner implements HouseAgencyCompany {
    
        @Override
        public void rentingHouse() {
            System.out.println("房东:签合同");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    2.2.1.2.3 消费者类
    
    /**
     * @Description:租客
     */
    public class Customer {
    
        HouseOwner houseOwner = new HouseOwner();
    
        @Test
        public void  needHouse(){
            System.out.println("=============================");
            //第一个参数:目标类的加载器,第二个参数:目标类的接口,第三个参数:调用处理器
            HouseAgencyCompany houseAgencyCompany = (HouseAgencyCompany) Proxy.newProxyInstance(
                    HouseAgencyCompany.class.getClassLoader(),
                    houseOwner.getClass().getInterfaces(),
                    new InvocationHandler() {
                        //第一个参数:代理类,
                        //第二个参数:执行的目标方法,这里指的是:rentingHouse
                        //第三参数:方法参数
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //前置增强
                            System.out.println("中介公司:我安排中介带你去看房子");
                            Object invoke = method.invoke(houseOwner, args);
                            //后置增强
                            System.out.println("中介公司:欢迎下次光临");
                            return invoke;
                        }
                    });
            houseAgencyCompany.rentingHouse();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    2.2.1.2.4 结果

    在这里插入图片描述

    2.2.2 cglib动态代理

    2.2.2.1 思考

    如果目标类没有实现接口呢?
    	那么就无法使用JDK的动态代理,因此这种方式有其局限性,必须实现一个接口。
    可以使用的方案:
    	使用CGLIB动态代理:基于子类(包含本类)
    
    • 1
    • 2
    • 3
    • 4

    net.sf.cglib.proxy.Enhancer

    Enhancer类是CGLib中的一个字节码增强器,作用用于生成代理对象,跟上一章所学的Proxy类相似,常用方式为:

     //方法1:该方法用于为指定目标类、回调对象 1、类的类型,2、调用处理器
     public static Object create(Class type, Callback callback)
    
    • 1
    • 2

    net.sf.cglib.proxy.MethodInterceptor

    ///第一个:代理类,第二个是:执行的目标方法,第三个:方法参数,第四个:方法的代理
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
    
    • 1
    • 2

    2.2.2.2实现

    2.2.2.2.1房东类
    /**
     * @Description:房东
     */
    public class HouseOwner {
    
        public void rentingHouse() {
            System.out.println("房东:签合同");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    2.2.2.2.2消费者类
    /**
     * @Description:租客
     */
    public class Customer {
    
        @Test
        public void  needHouse(){
            //第一个参数:被代理类的类型 第二个参数:拦截方法
            HouseOwner houseOwner = (HouseOwner) Enhancer.create(
                    HouseOwner.class,
                    new MethodInterceptor() {
                        @Override
                        //第一个:代理类,第二个是:执行的目标方法,第三个:方法参数,第四个:方法的代理
                        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                            System.out.println("房东儿子:我爸身体不好,让我来带i看房子");
                            methodProxy.invokeSuper(o, objects);
                            System.out.println("房东儿子:我爸身体不好,后面有什么事情你可以找我");
                            return null;
                        }
            });
            houseOwner.rentingHouse();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    2.2.2.2.3结果

    在这里插入图片描述

    2.2.2.3小结

    1、Cglib动态代理:基于类,无需实现接口;
    2、被代理的目标类不能被final修饰
    
    • 1
    • 2

    2.2.3动态代理小结

    通过动态代理可以完成对已有方法的功能的增强:
    1、JDK动态代理
    	要求:
    		被代理对象至少实现一个接口
    	应用场景:
    		被代理对象有接口
    
    2、CGLIB动态代理
    	要求:
    		被代理类上不能用static、final修饰
    	应用场景:
    		被代理对象没有实现接口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    爬虫实现自己的翻译服务器
    Linux命令--定时运行程序(脚本)--方法/实例
    一篇五分生信临床模型预测文章代码复现——文章介绍
    基于OPENCV和图像减法的PCB缺陷检测
    不用USB,通过adb无线调试安卓手机页面
    [附源码]计算机毕业设计JAVA大学生评奖评优系统
    sql12(Leetcode1280学生们参加各科测试的次数)
    android Studio 文件设置作者信息
    LS-dnay 压力显示异常
    3D激光点云霍夫变换拟合直线
  • 原文地址:https://blog.csdn.net/weixin_43811057/article/details/125416301