• java动态代理


    "代理"这个词相信大家并不陌生,简单来说就是代替厂家来售卖商品,代理替代厂家售卖商品,顾客找代理购买商品。也就是说:1)顾客和厂家之间是不可见的,顾客不知道背后的厂家是谁。2)代理可以对顾客进行“定位”,更精确的售卖给需要的客户群体。

    代理模式

    代理模式:为其他对象提供一种代理以控制对这个对象的访问,也就是创建一个代理对象作为客户端和目标对象之间的中介,主要目的就是保护目标对象或增强目标对象

    通过使用代理模式,通常有以下两个优点:

    \1) 可以隐藏被代理类的实现

    \2) 可以实现客户与被代理类间的解耦,在不修改被代理类代码的情况下能够做一些额外的处理

    静态代理

    所谓的动态代理,就是通过声明一个明确的代理类来访问源对象,一个代理只能服务于一种产品,当有n种产品时,就需要n个代理,这样就不利于业务的发展。

    举例:我们有两个接口,Mouse和Keyboard,每个接口都有一个实现类

    imgimg

    实现类中的代码如下:

    public class LogitechMouse implements Mouse{
    
        @Override
        public void sell() {
            System.out.println("出售罗技鼠标");
        }
        
    }
    public class HHKBKeyboard implements Keyboard{
    
        @Override
        public void sell() {
            System.out.println("出售HHKB键盘");
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    现在我们要做的就是让代理在调用sell()前输出一句售前了解,调用后输出一句售后服务

    那我们只需写两个代理类MouseProxyKeyboardProxy

    public class MouseProxy implements Mouse {
        private Mouse mouse;
    
        public MouseProxy(Mouse mouse) {
            this.mouse = mouse;
        }
    
        @Override
        public void sell() {
            System.out.println("售前了解");
            mouse.sell();
            System.out.println("售后服务");
        }
    }
    public class KeyboardProxy implements Keyboard{
    
        private Keyboard keyboard;
    
        public KeyboardProxy(Keyboard keyboard) {
            this.keyboard = keyboard;
        }
    
        @Override
        public void sell() {
            System.out.println("售前了解");
            keyboard.sell();
            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
    • 27
    • 28
    • 29

    最终执行为:

    public class Test {
    
        public static void main(String[] args) {
    
            Mouse logitechMouse = new LogitechMouse();
            MouseProxy mouseProxy = new MouseProxy(logitechMouse);
            mouseProxy.sell();
    
            Keyboard hhkbKeyboard = new HHKBKeyboard();
            KeyboardProxy keyboardProxy = new KeyboardProxy(hhkbKeyboard);
            keyboardProxy.sell();
        }
        
    }
    --------------------------
    输出:
    售前了解
    出售罗技鼠标
    售后服务
    售前了解
    出售HHKB键盘
    售后服务
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    静态代理的代码非常简单易懂,这种模式虽好,但是也有明显的缺点:

    • 会存在大量冗余的代理类,这里只有两个接口,如果有n个接口,那么就要定义n个代理类。
    • 不易维护,一旦接口更改,代理类和被代理类都要更改。

    那么这个时候就可以使用动态代理来解决了

    动态代理

    代理类在程序运行时创建代理的方式叫动态代理,也就是说代理类并不是在java代码中定义的,而是在运行的时候动态生成的

    JDK动态代理

    JDK从1.3版本就开始支持动态代理类的创建。主要核心类只有2个:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

    还是上面的例子,用JDK动态代理如下:

    public class JDKProxy implements InvocationHandler {
    
        private Object object;
    
        public JDKProxy(Object object) {
            this.object = object;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("售前了解");
            Object invoke = method.invoke(object, args);
            System.out.println("售后服务");
            return invoke;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,

    代理类对象作为proxy参数传入,

    参数method标识了我们具体调用的是代理类的哪个方法,

    args为这个方法的参数。

    这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。因此我们可以在中介类的invoke方法中实现输出售前了解,再调用被代理类的方法,再输出售后服务

    img

    执行代码

    public class Test {
    
        public static void main(String[] args) {
    
            Mouse logitechMouse = new LogitechMouse();
            JDKProxy jdkProxy = new JDKProxy(logitechMouse);
            Mouse mouse= (Mouse)Proxy.newProxyInstance(jdkProxy.getClass().getClassLoader(), new Class[]{Mouse.class}, jdkProxy);
            mouse.sell();
    
            HHKBKeyboard hhkbKeyboard = new HHKBKeyboard();
            JDKProxy jdkProxy1 = new JDKProxy(hhkbKeyboard);
            Keyboard keyboard = (Keyboard)Proxy.newProxyInstance(jdkProxy1.getClass().getClassLoader(), new Class[]{Keyboard.class}, jdkProxy1);
            keyboard.sell();
    
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    可以看到无论多少个接口,只需要一个代理类就可以了。

    CGLIB动态代理

    代理类:

    public class CGLIBProcy implements MethodInterceptor {
    
        private Enhancer enhancer = new Enhancer();
    
        private Object object;
    
        public CGLIBProcy(Object object) {
            this.object = object;
        }
    
        public Object getProxy(){
            //设置需要创建子类的类
            enhancer.setSuperclass(object.getClass());
            enhancer.setCallback(this);
            //创建代理对象
            return enhancer.create();
        }
        // o: cglib 动态生成的代理类的实例
        // method:实体类所调用的都被代理的方法的引用
        // objects 参数列表
        // methodProxy:生成的代理类对方法的代理引用
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("售前了解");
            Object invoke = method.invoke(object, objects);
            System.out.println("售后处理");
            return invoke;
        }
    }
    
    • 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

    执行代码:

    public class Test {
    
        public static void main(String[] args) {
    
            Mouse logitechMouse = new LogitechMouse();
            CGLIBProcy cglibProcy = new CGLIBProcy(logitechMouse);
            Mouse proxy = (Mouse)cglibProcy.getProxy();
            proxy.sell();
    
            cglibProcy = new CGLIBProcy(new HHKBKeyboard());
            Keyboard keyboard = (Keyboard)cglibProcy.getProxy();
            keyboard.sell();
    
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    JDK代理与CGLIB代理的区别

    • JDK动态代理实现接口,CGLIB动态继承思想
    • JDK动态代理(目标对象存在接口时)执行效率高于CIGLIB
    • 如果对象有接口实现,选择JDK代理,如果没有接口实现选择CGILB代理
  • 相关阅读:
    MindSpore数据集加载-GeneratorDataset卡住、卡死
    【C++篇】AVL树
    ceph简介
    IDEA项目取消git版本管控并添加svn版本控制
    智能电销机器人,主要体现的价值是什么
    cookie,session,Token 这些你都知道吗?
    antd-React Table 中文转化
    微服务feign组件学习
    【OpenCV】角点检测、特征点提取(Harris、Shi-Tomas、SIFT、SURF、FAST、ORB)学习笔记
    【软考 系统架构设计师】案例分析⑧ 数据库索引与视图
  • 原文地址:https://blog.csdn.net/qq_43460095/article/details/125602607