• Java代理模式


    java代理模式

    代理模式: 目标对象不可直接访问,而需要通过代理对象进行访问,在这过程中代理对象可以增强目标对象访问的功能进行增强(可以理解为额外添加一下功能)。

    生活中:
    找房:
    房东 ===> 目标对象;
    房屋中介 ===> 代理对象;
    你,我 ===> 客户端对象;
    
    买衣服:
    服装生产厂===>目标对象;
    门店===>代理对象;
    你,我===>客户端对象;
    
    开发中:
    发送短信功能:
    运营商(移动,电信,联通)===>目标对象;
    第三方公司===>代理对象;
    开发的应用程序需要发送短信的功能===>客户端对象;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    代理模式的作用:

    • 控制目标对象的访问;
    • 增强功能;

    代理模式的分类:

    • 静态代理;
    • 动态代理;
      • JDK动态代理;
      • CGLib动态代理(子类代理);

    1. 静态代理

    静态代理是代理模式的一种,它具备以下特点:

    • 目标对象和代理对象必须实现同一个业务接口;
    • 代理对象在程序运行之前就已经存在;
    • 能够灵活的进行目标对象的切换,却无法进行功能的灵活扩展(使用动态代理解决此问题);
    • 适合用于目标对象功能固定目标对象灵活切换的场景;

    1.1 静态代理的实现:

    a>理论原理

    加入有这个业务功能:请明星进行节目表演。

    明星刘德华,即目标对象(无法直接访问);
    刘德华代理:代理对象(我们可以访问,他也可以直接跟明星进行对接);
    我们:客户端对象
    
    • 1
    • 2
    • 3

    image-20220808071905479

    b>代码实现

    业务接口:

    package com.xuan;
    
    /**
     * 唱歌的业务类,目标对象与代理对象需要同时实现该接口。
     */
    public interface SingService {
        void sing();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    目标对象:

    package com.xuan.impl;
    
    import com.xuan.SingService;
    
    /**
     * 唱歌业务的目标对象:刘德华,主要是实现具体的业务。
     */
    public class SuperStarLiu implements SingService {
    
        @Override
        public void sing() {
            System.out.println("我是刘德华,我正在唱歌!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    代理对象:

    package com.xuan.impl;
    
    import com.xuan.SingService;
    
    /**
     * 唱歌业务的代理对象:刘德华的助理,主要是完成除了主业务之外的工作,如场地预约、时间安排、费用结算等。
     */
    public class Agent implements SingService {
        @Override
        public void sing() {
            System.out.println("Agent场地预约");
            System.out.println("Agent时间安排");
            // 主业务
            SuperStarLiu starLiu = new SuperStarLiu();
            starLiu.sing();
            System.out.println("Agent费用结算");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    客户端对象:

    package com.xuan;
    
    import com.xuan.impl.Agent;
    import org.junit.Test;
    
    public class MyTest {
        @Test
        public void testClient(){
            /*
             要求明星唱歌,这里不能直接与明星进行沟通,必须与他的代理进行协商。
             */
            // 使用多态
            SingService singService = new Agent();
            singService.sing();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1.2 面向接口编程(重要)

    类中的成员变量设计为接口,方法的形参设计为接口,方法的返回值设置为接口,程序在调用时会自动指向接口的实现类。

    1.3 使用接口编程实现目标对象的灵活切换

    public class Agent implements SingService {
        // 目标对象
        private SingService target;
    
        public Agent() {
        }
        // 传入目标对象,方法设计为接口,有利于程序的灵活切换。
        public Agent(SingService target) {
            this.target = target;
        }
    
        @Override
        public void sing() {
            System.out.println("Agent场地预约");
            System.out.println("Agent时间安排");
            // 主业务
            target.sing(); // 面向接口编程,调用时会自动指向接口具体的实现类。
            System.out.println("Agent费用结算");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    @Test
    public void testClient02(){
        // 使用面向接口编程,这里需要用户提供一个需要唱歌的明星。
        SingService singService = new Agent(new SuperStarZhou());
        singService.sing();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2. JDK动态代理

    代理对象在程序运行的过程中动态在内存构建,可以灵活地进行业务功能的扩展。JDK动态代理具有以下特性:

    • 目标对象必须实现业务接口;
    • JDK代理对象不需要实现业务接口;
    • JDK动态代理的对象在程序运行前不存在,在程序运行时动态在内存中构建;
    • JDK动态代理可以灵活地进行业务功能能的切换;
    • 接口实现类中的方法(非接口中的方法)不能被代理,即适用于接口实现类方法增强的情况;

    2.2 JDK动态代理用到的类和接口

    它是使用现有的工具类来完成JDK动态实现的;

    a>Proxy类

    它是java.lang.reflect.Proxy包下的类. 它有一个方法Proxy.newProxyInstance(…)专门用来生成动态代理对象。

    public static Object newProxyInstance(
      	ClassLoader loader,  //类加载器
        Class<?>[] interfaces,//目标对象实现所实现的接口
        InvocationHandler h //它就类似于Agent的功能,代理的功能和目标对象的业务功能调用在这
        )throws IllegalArgumentException{
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    b>Method类

    反射用的类,用来对目标对象的方法进行反射调用。

    c>InvocationHandler接口

    它是实现代理和业务功能的,我们在调用时使用匿名内部实现。

    2.3 代码实现

    package com.xuan.proxy;
    
    import com.xuan.service.SingService;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 代理工厂。
     */
    public class ProxyFactory {
        // 类中的成员变量(即目标对象)设计为接口
        SingService target;
    
        // 传入目标对象
        public ProxyFactory(SingService target){
            this.target = target;
        }
    
        // 返回动态代理对象
        public Object getAgent(){
            return Proxy.newProxyInstance(
                    // 类加载器,完成目标对象的加载
                    target.getClass().getClassLoader(),
                    // 目标对象实现的所有接口
                    target.getClass().getInterfaces(),
                    // 实现代理功能的接口,这里传入匿名内部实现
                    new InvocationHandler() {
                        @Override
                        public Object invoke(
                                // proxy就是创建的动态代理对象
                                Object proxy,
                                // method就是目标对象所要执行的方法
                                Method method,
                                // 目标方法的参数
                                Object[] args) throws Throwable {
                            // 代理对象所实现的功能
                            System.out.println("预定时间");
                            System.out.println("预定场地");
                            // 主业务功能实现,注意这里不能写具体的方法而是使用method的invoke()方法,该方法会返回所调用的方法的返回值。
                            Object obj = method.invoke(target, args);
                            System.out.println("金费的预算");
                            // 重点主要,该返回值是调用的方法的返回值
                            return obj; 
                        }
                    });
        }
    }
    
    • 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
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    public class MyTest {
        @Test
        public void test01(){
            ProxyFactory proxy = new ProxyFactory(new SuperStarLiu());
            SingService agentLiu = (SingService) proxy.getAgent();
    //        proxyAgent.sing();
            System.out.println(agentLiu.perform("沿河土家族"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3. CGLib动态代理

    CGLib代理又称为子类。通过动态的在内存中构建子类对象,重写父类的方法进行代理功能的增强。如果目标对象没有实现接口,则只能通过CGLib子类代理来进行功能增强。子类代理是对象字节码框架ASM来实现的。注意:

    • 被代理的类不能为final, 否则报错;
    • 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法;
      public Object getProxyInstance(){		
    		//1.使用工具类
    		Enhancer en=new Enhancer();
    		//2.设置父类
    		en.setSuperclass(target.getClass());
    		//3.设置回调函数
    		en.setCallback(this);
    		//4.创建子类(代理)对象
    		return en.create();  ===>返回的是子类代理对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    Python中的科学计算和数学建模
    学习在echarts中优化数据视图dataView样式带表格样式,支持复制功能
    6.Android应用架构指南:界面层界面事件
    【知识图谱】概述
    47-5 内网渗透 - 提权环境搭建
    用 p5.js 写个五子棋
    智能生产线数字孪生有什么特点?AR智慧运维供应商首选广州华锐互动
    Python_操作记录
    android 7.1 mipi 屏 唤醒白屏
    Java核心编程学习 -- day7
  • 原文地址:https://blog.csdn.net/Hello_super/article/details/126229782