• 代理模式——动静态代理,面向接口编程


    一、代理模式定义:

    • 目标对象不可访问,通过代理对象增强功能访问。
    • 举个例子:
      • 房东==>目标对象
      • 房屋中介==>代理对象
      • 租客==>客户端

    二、代理模式的作用:

    • 控制目标对象的访问。
      • 举个例子:为了避免麻烦,房东(目标对象)拒绝与租客(客户端)频繁见面,房屋中介(代理对象)也会控制你与房屋中介(代理对象)见面。
    • 增强功能。
      • 举个例子:房屋中介(代理对象)会给租客(客户端)提供一系列房东(目标对象)提供不了的服务(功能)。

    三、代理模式分类:

    • 子类代理。
    • 静态代理。
    • 动态代理。

    四、子类代理模式:

    1. 特点:
    • 通过子类继承父类,子类在父类的基础上进行功能业务的增加。
    1. 子类代理实现:
    • 父类BookService :
    public class BookService {
        public void buy(){
            System.out.println("购书业务...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 子类SBookService:
    public class SBookService extends BookService{
        public void buy(){
            System.out.println("购书前业务...");//增加功能
            super.buy();//使用父类业务功能
            System.out.println("购书完成...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    五、静态代理模式:

    1. 特点:
    • 目标对象和代理对象实现同一个业务接口。
    • 目标对象必须实现接口。
    • 代理对象在程序运行前就已经存在。
    • 能够灵活地进行目标对象的切换,却无法进行功能的灵活处理。(使用动态代理解决此问题)
    • 代理对象无法实现目标对象的功能,却可以在目标对象的基础上增加其他功能。
    1. 静态代理实现:

    在这里插入图片描述

    • 业务接口:
    package com.user.cailinhao.service;
    
    /**
     * 业务接口
     */
    public interface Service {
        //规定唱歌的业务功能
        void sing();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 目标对象1:刘德华
    package com.user.cailinhao.service.impl;
    
    import com.user.cailinhao.service.Service;
    
    /**
     * 刘德华:目标对象1
     */
    public class SuperStarLiu implements Service {
        @Override
        public void sing() {
            System.out.println("我是刘德华,我正在表演");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 目标对象2:周润发
    package com.user.cailinhao.service.impl;
    
    import com.user.cailinhao.service.Service;
    
    /**
     * 周润发:目标对象2
     */
    public class SuperStarZhou implements Service {
        @Override
        public void sing() {
            System.out.println("我是周润发,我正在表演");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 代理对象:假设刘德华和周润发公用同一个助理
    package com.user.cailinhao.service.impl;
    
    import com.user.cailinhao.service.Service;
    
    /**
     * 助理:代理对象
     */
    public class Agent implements Service {
        //面向接口编程,类中的成员变量设计为接口
        public Service target;
        //面向接口编程,方法的参数设计为接口,传入目标对象,完成代理功能
        public Agent(Service target) {
            this.target = target;
        }
    
        @Override
        public void sing() {
            System.out.println("预定时间");
            System.out.println("预定场地");
            //重点:业务功能必须由目标对象亲自实现
            /*new SuperStarLiu().sing();这样就写死了,不灵活*/
            //请谁谁唱,很灵活
            target.sing();
            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
    • 24
    • 25
    • 26
    • 客户端:
    package com.user.cailinhao.test;
    
    import com.user.cailinhao.service.Service;
    import com.user.cailinhao.service.impl.Agent;
    import com.user.cailinhao.service.impl.SuperStarLiu;
    import org.junit.Test;
    
    public class MyTest {
        @Test
        public void testAgent(){
            /*
            Service agent = new Agent();
            agent.sing();
            */
        }
        public void testAgent1(){
            //面向客户,具体谁来唱由客户选择
            Service agent = new Agent(new SuperStarLiu());
            agent.sing();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    六、动态代理模式:

    1. 特点:
    • 静态代理存在的问题:Service接口中如果添加其他方法,比如表演show(),会使得实现接口的目标对象和代理对象的代码都要改变。
    • 代理对象在程序运行的过程中动态在内存构建,可以灵活地进行业务功能的切换。

    七、JDK动态代理

    1. 特点:
    • 目标对象必须实现业务接口。
    • JDK代理对象不需要实现业务接口。
    • JDK动态代理的对象在程序运行前不存在。在程序运行时动态地在内存中构建。
    • JDK动态代理灵活地进行业务功能的切换。
    • 目标对象中的非接口中的方法不能被代理。
    1. 重点类说明:
    • Proxy类:
      它是java.lang.reflect.Proxy包下的类,它有一个方法Proxy.newProxy.newProxyInstance(…)专门用来生成动态代理对象
    public static Object newProxyInstance(ClassLoader lodaer,//类加载器,完成目标对象的加载
    									  Class<?>[] interfaces,//目标对象实现的所有接口
    									  InvocationHandler h,//类似于静态代理的Agent功能,代理的功能和目标对象功能的调用在这里
    									  ) throws IllegalArgumentException
    {.......}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • Method类:
      反射机制,用来进行目标对象方法的反射调用

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

    1. JDK动态代理实现:
    • 代理工厂:重点!!!!!!!!!!!
    package com.user.cailinhao.proxy;
    
    import com.user.cailinhao.service.Service;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    /**
     * 代理工厂用于动态生成代理对象Agent,通过传入target参数生成一个专门为target目标对象代理的Agent,通过实现接口的invoke方法实现代理对象Agent的额外功能
     */
    public class ProxyFactory {
        //面向接口编程,类中的成员变量设计为接口
        Service target;
        //面向接口编程,方法的参数设计为接口,传入目标对象,完成代理功能
        public ProxyFactory(Service target) {
            this.target = target;
        }
        //返回动态代理对象
        public Object getAgent(){
            return Proxy.newProxyInstance(//获取动态代理对象用这个方法
                    target.getClass().getClassLoader(),//类加载器,在JVM中完成目标对象的加载
                    target.getClass().getInterfaces(),//目标对象实现的所有接口,为了获取所有接口中的方法
                    new InvocationHandler() {//这是一个实现代理功能的接口,类似于静态代理的Agent功能,代理的功能和目标对象功能的调用在这里,采用匿名内部类实现
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//返回:目标对象方法的返回值
                            //proxy是创建好的代理对象
                            //method是目标对象的某个方法,可能是sing()、show()
                            //args是目标对象的方法的参数
    
    
                            //代理功能
                            System.out.println("预定时间");
                            System.out.println("预定场地");
                            //目标对象的功能
                            //target.sing();这么些不行,还是写死了,等同静态代理
                            Object obj = method.invoke(target,args);//反射机制给方法传参数,调用target对象的invoke方法并传参数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
    • 业务接口:
    package com.user.cailinhao.service;
    
    /**
     * 业务接口
     */
    public interface Service {
        void sing();
        void show();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 目标对象1:刘德华
    package com.user.cailinhao.service.impl;
    
    import com.user.cailinhao.service.Service;
    
    /**
     * 目标对象1:刘德华
     */
    public class SuperStarLiu implements Service {
        @Override
        public void sing() {
            System.out.println("我是刘德华,我在唱歌");
        }
    
        @Override
        public void show() {
            System.out.println("我是刘德华,我在表演");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 目标对象2:周润发
    package com.user.cailinhao.service.impl;
    
    import com.user.cailinhao.service.Service;
    /**
     * 目标对象2:周润发
     */
    public class SuperStarZhou implements Service {
        @Override
        public void sing() {
            System.out.println("我是周润发,我在唱歌");
        }
    
        @Override
        public void show() {
            System.out.println("我是周润发,我在表演");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 客户端:
    package com.user.cailinhao.test;
    
    import com.user.cailinhao.proxy.ProxyFactory;
    import com.user.cailinhao.service.Service;
    import com.user.cailinhao.service.impl.SuperStarLiu;
    import org.junit.Test;
    
    public class MyTest {
        @Test
        public void testJDK(){
            ProxyFactory factory = new ProxyFactory(new SuperStarLiu());//目标对象创建代理对象
            Service agent = (Service) factory.getAgent();//!!!!!强转成Service接口才能调用sing方法
            agent.sing();
        }
        @Test
        public void testJDK1(){
            ProxyFactory factory = new ProxyFactory(new SuperStarLiu());//目标对象创建代理对象
            Service agent = (Service) factory.getAgent();//!!!!!强转成Service接口才能调用sing方法
            agent.show();
        }
        @Test
        public void testJDK2(){
            ProxyFactory factory = new ProxyFactory(new SuperStarLiu());//目标对象创建代理对象
            Service agent = (Service) factory.getAgent();//!!!!!强转成Service接口才能调用sing方法
            System.out.println(agent.getClass());//class jdk.proxy2.$Proxy4,这是一个JDK动态代理对象的类型
        }
    }
    
    
    • 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

    八、CBLib动态代理:

    1. 特点:
    • 又称为子类动态代理,通过动态地在内存中构建子类对象,重写父类的方法进行代理功能的增强。
    • 如果目标对象没有实现接口,则只能通过CGLib自雷代理来进行功能增强。
    1. CBLib动态代理实现:
    • 业务接口:
    package com.cailinhao.service;
    
    /**
     * 业务接口
     */
    public interface Service {
        void sing();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 目标对象:
    package com.cailinhao.service.impl;
    
    import com.cailinhao.service.Service;
    
    /**
     * 目标对象1:刘德华
     */
    public class SuperStarLiu implements Service {
        @Override
        public void sing() {
            System.out.println("我是刘德华,我在唱歌");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 代理对象:
    package com.cailinhao.service.sub;
    
    import com.cailinhao.service.impl.SuperStarLiu;
    
    /**
     * 代理对象
     */
    public class SubSuperStarLiu extends SuperStarLiu {
        @Override
        public void sing() {
            System.out.println("预定时间");
            System.out.println("预定地点");
            super.sing();
            System.out.println("结算费用");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 客户端:
    package com.cailinhao;
    
    import com.cailinhao.service.impl.SuperStarLiu;
    import org.junit.Test;
    
    public class MyTest {
        @Test
        public void testAgent(){
            SuperStarLiu liu = new SuperStarLiu();
            liu.sing();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    九:面向接口编程:

    • 类中的成员方法设计成接口。
    • 方法的参数设计为接口。
    • 方法返回值设计为接口。
    • 使用时使接口指向实现类。
  • 相关阅读:
    命令与文件的查找
    .NET C#基础(6):命名空间 - 有名字的作用域
    idea如何隐藏background tasks
    EN 14915实木镶板和包层—CE认证
    面试算法31:最近最少使用缓存
    pat basic 1050 螺旋矩阵
    2023-2028年中国六氟化钨市场发展态势及投资建议报告
    合理使用DTO(Data Transfer Object)
    从理解js双重递归执行顺序到用递归方式实现二叉树中序遍历
    一文搞懂 Netty 发送数据全流程 | 你想知道的细节全在这里
  • 原文地址:https://blog.csdn.net/m0_53881899/article/details/126789455