返回顶部
首页 > 资讯 > 后端开发 > Python >浅谈Java动态代理的实现
  • 814
分享到

浅谈Java动态代理的实现

2024-04-02 19:04:59 814人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录一、代理设计模式1.1 什么是代理1.2 代理模式入门二、Java代理的三种实现2.1 静态代理2.2 Java自带的动态代理2.3 cglib实现动态代理三

一、代理设计模式

1.1 什么是代理

  • 考虑真实的编程场景,项目中存在一个访问其他数据源的接口,包含一个query()方法
  • 我们已经针对这个接口,实现了MysqlHiveHBasemongoDB等作为数据源的实现类
  • 但是,在测试过程中,我们发现这些数据源的查询并不是很稳定
  • 最原始的想法: 在所有实现类query()方法中,代码首部获取startTime,代码尾部获取endTime,通过打印日志的方式,知道每次查询的耗时

long startTime = System.currentTimeMillis();
logger.info("query mysql start:" + new Date(startTime).toLocaleString());
// 具体的query代码
...

long endTime = System.currentTimeMillis();
logger.info(String.fORMat("query mysql end: %s, consumed time: %dms", new Date(endTime).toLocaleString(), (endTime-startTime)));
  • 直接修改已经实现的方法,存在很多缺点:

(1)现在是打印日志,代码非常简单,就算query()方法不是你实现的,你也能很好的完成。
(2)但是如果是其他功能呢?比如,如果查询失败,要求你查询重试

  • 因此,在不改变已经实现好的query()方法前提下,去实现日志打印的功能是最好的方法。
  • 进阶想法: 我为每个实现类创建一个包装类。

(1)这个包装类与实现类一样,实现了相同的接口。
(2)在query()方法中,直接调用实现类的query()方法,并在调用前后进行日志打印
(3)对实现类方法的调用,都改成对包装类方法的调用


long startTime = System.currentTimeMillis();
logger.info("query mysql start:" + new Date(startTime).toLocaleString());
// 使用try-finally
JSONObject[] data = null;
try {
    data = mysql.query();
    return data;
} catch (Exception exception) {
    throw exception;
} finally {
    long endTime = System.currentTimeMillis();
    logger.info(String.format("query mysql end: %s, consumed time: %dms", new Date(endTime).toLocaleString(), (endTime - startTime)));
}
  • 这时,代理模式的概念就变得非常清晰了:不直接调用实现类的某个方法,而是通过实现类的代理去调用。
  • 这样不仅可以实现调用者与被调用者之间的解耦合,还可以在不修改调用者的情况下,丰富功能逻辑

1.2 代理模式入门

代理模式的UML图如下

1.subject: 抽象主题角色,是一个接口,定义了一系列的公共对外方法

2.real subject: 真实主题角色,也就是我刚刚提到的实现类,又称委托类。
委托类实现抽象主题,负责实现具体的业务逻辑

3.proxy: 代理主题角色,简称代理类。它也实现了抽象主题,用于代理、封装,甚至增强委托类。
一般通过内含委托类,实现对委托类的封装

4.client: 当访问具体的业务逻辑时,clinet表面是访问代理类,而实际是访问被代理类封装的委托类

在这里插入图片描述 

代理模式的应用场景:目前,就我本人所接触的使用场景,就是通过代理去打印日志、增强业务逻辑 😂

二、Java代理的三种实现

2.1 静态代理

  • 所谓的静态动态,是相对于字节码的生成时机来说的:

(1)静态是指字节码,即class文件,在编译时就已经生成。
(2)动态是指字节码在运行时动态生成,而不是编译时提前生成

  • 刚刚,我们针对数据查询的进阶方法,实际就是静态代理
  • 通过为每个委托类创建对应的代理类,然后编译时就可以得到代理类的字节码

下面是一个具体的静态代理实例:

抽象主题:


public interface Animal {
   void eat();
}

委托类:Dog和Cat,实现了抽象接口


public class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("I like eating bone");
    }
}

public class Cat implements Animal {
    @Override
    public void eat() {
        System.out.println("I like eating fish");
    }
}

代理类:代理类中含有对应的委托类,通过调用委托类的具体实现,来封装委托类


public class DogProxy implements Animal {
    private Dog dog;

    public DogProxy(Dog dog) {
        this.dog = dog;
    }

    @Override
    public void eat() {
        System.out.print("I'm a "+dog.getClass().getSimpleName() +". ");
        dog.eat();
    }
}

public class CatProxy implements Animal {
    private Cat cat;

    public CatProxy(Cat cat) {
        this.cat = cat;
    }

    @Override
    public void eat() {
        System.out.print("I'm a " + cat.getClass().getSimpleName()+". ");
        cat.eat();
    }
}

静态代理虽然实现简单、不更改原始的业务逻辑,但是仍然存在以下缺点:

1.如果存在多个委托类,则需要创建多个代理类,这样则会产生过多的代理类

2.如果抽象主题增加、删除、修改方法时,委托类和代理类都需要同时修改,不易维护

2.2 Java自带的动态代理

  • 静态代理存在以上缺点,如果我们能在程序运行时,动态生成对应的代理类,则无需创建并维护过多的代理类
  • 使用Java自带的java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler类,可以实现动态代理
  • 从类的全路径可以看出,Java的动态代理实际利用的是反射机制实现的
  • 其中,Proxy用于创建对应接口的代理类。具体代理的是哪个委托类,是由实现InvocationHandler接口的中介类决定的
  • 代理类、委托类、中介类之间的关系如下

在这里插入图片描述

Java自带的动态代理具体实现:

1.创建抽象主题 —— 与静态代理类一致,不再展示

2.创建实现类 —— 与静态代理类一致,不再展示

3.实现InvocationHandler接口,创建中介类


public class AnimalInvokeHandler implements InvocationHandler {
    private Animal animal;

    public AnimalInvokeHandler(Animal animal) {
        this.animal = animal;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy class: " + proxy.getClass().getName());
        System.out.printf("proxy instanceof  Animal: %b \n", proxy instanceof Animal);
        System.out.printf("---- Call method: %s, class: %s ----\n", method.getName(), animal.getClass().getSimpleName());
        // 通过反射,调用委托类的方法
        Object result = method.invoke(animal, args);
        return result;
    }
}

4.通过Proxy创建动态代理类,实现对抽象主题的代理


public static void main(String[] args) {
    // 通过Proxy.newProxyInstance创建代理类
    // 将代理类转为抽象主题,可以动态的创建实现了该主题的代理类
    // 必须从实现类去获取需要代理的接口
    // 指定中介类,通过中介类实现代理
    Animal proxy = (Animal) Proxy.newProxyInstance(Main.class.getClassLoader(), Dog.class.getInterfaces(), new AnimalInvokeHandler(new Dog()));
    proxy.eat();
}

执行结果:

在这里插入图片描述

Java原生的动态代理,利用反射动态生成代理类字节码ProxyX.class,然后将其强制转化为抽象主题类型,就能实现对该接口的代理

jdk动态代理之所以只能代理接口是因为代理类本身已经extends了Proxy,而java是不允许多重继承的,但是允许实现多个接口

Java原生动态代理,又叫jdk动态代理,具有以下优缺点

1.优点: jdk动态代理,避免了静态代理需要创建并维护过多的代理类的

2.缺点: jdk动态代理只能代理接口,因为Java的单继承原则:代理类本身已经继承了Proxy类,就不能再继承其他类,只能实现委托类的抽象主题接口。

2.3 cglib实现动态代理

  • jdk动态代理存在只能代理接口的问题,是十分不方便的。
  • 考虑以下场景:

一个类中有两个方法methodA:转账到其他账户,methodB:查询账户余额。
如果用户访问methodA,希望先提示用户检查账户信息是否正确;
如果用户访问methodB,希望在用户查询完余额后,提示用户关注银行的微信公众号。

  • 上述类已经成功用于业务场景了,我们想要实现这些增强功能,最好不要更改其原始代码,而是通过代理实现功能增强
  • 这时,便可以使用cglib实现对类的动态代理 —— 小白看了实现后,可能会倾向于说这就是类似JAVA WEB的拦截器 😂

三、cglib动态代理的实现

添加Maven依赖


<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.11</version>
</dependency>

简化版的银行系统类


public class BankSystem {
    // 转账
    public boolean transferAccount(double amount, String address) {
        System.out.printf("Send %f dollars to account %s", amount, address);
        // 转账成功
        return true;
    }

    // 查询账户余额
    public String queryBalance(){
        System.out.printf("Query account balance success");
        return "Account balance: 2400 dollars";
    }
}

为转账方法创建拦截器


public class TransferInterceptor implements MethodInterceptor {
    
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before(objects);
        Object result = methodProxy.invokeSuper(o, objects);
        return result;
    }

    private void before(Object[] args){
        System.out.printf("Please check: you will send %.2f dollar to account %s.\n", args[0], args[1]);
    }
}

为余额查询方法创建拦截器


public class QueryBalanceInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 调用预发插叙方法
        Object result = proxy.invokeSuper(obj, args);
        after();
        return result;
    }

    private void after() {
        System.out.printf("Please pay attention to WeChat official account.\n");
    }
}

创建filter,实现方法与拦截器的映射


public class BankFilter implements CallbackFilter {
    @Override
    public int accept(Method method) {
        if ("transferAccount".equals(method.getName())) {
            // 使用拦截器列表中的第1个拦截器
            return 0;
        }
        // 使用拦截器列表中的第2个拦截器
        return 1;
    }
}

使用cglib实现动态代理 —— 我认为就是方法拦截 😂


public static void main(String[] args) {
    // 新建拦截器
    TransferInterceptor transferInterceptor = new TransferInterceptor();
    QueryBalanceInterceptor queryBalanceInterceptor = new QueryBalanceInterceptor();

    // 创建工具类
    Enhancer enhancer = new Enhancer();
    // 设置委托类,在cglib中,这是cglib需要继承的超类
    enhancer.setSuperclass(BankSystem.class);
    // 设置多个拦截器
    enhancer.setCallbacks(new Callback[]{transferInterceptor, queryBalanceInterceptor});
    // 实现拦截器和方法的映射,即为不同的方法配置不同的拦截器
    enhancer.setCallbackFilter(new BankFilter());

    // 创建代理类
    BankSystem proxy = (BankSystem) enhancer.create();

    // 执行转账,调用TransferInterceptor
    boolean ok = proxy.transferAccount(1024.28, "lucy");
    System.out.println("transfer money success: " + ok);
    // 查询余额,调用QueryBalanceInterceptor
    proxy.queryBalance();
}

cglib总结

1.优点1: cglib基于实现动态代理,通过ASM字节码框架动态生成委托类的子类,并使用方法拦截器实现对委托类方法的拦截。

2.优点2: 基于ASM字节码框架动态生成代理类,比jdk动态生成代理类更加高效

3.缺点: 通过继承委托类创建动态代理类,因此不能代理final委托类或委托类中的final方法

四、面试常见问题

java中代理的实现

共有三种方法:静态代理、JDK动态代理、cglib动态代理

  • 静态代理: 为每个实现类都创建一个对应的代理类,需要创建并维护大量的代理类
  • jdk动态代理: 通过Proxy.newProxyInstance()为抽象主题创建代理类,被代理的委托类包含在InvocationHandler类中,由InvocationHandler类的invoke方法通过反射实现对委托类方法的调用
  • cglib动态代理:通过ASM字节码框架,继承委托类以创建代理类。代理类通过方法拦截器,实现对委托类方法的拦截

三种代理方式的比较

1.静态代理,需要创建并维护大量的委托类

2.jdk动态代理,避免了静态类的上述缺点,但只能代理接口(Java单继承原则,代理类已经继承了Proxy类)

3.cglib动态代理,可以实现对类的代理,并通过方法拦截器实现对委托类(父类)方法的拦截;使用强大的ASM字节码框架,更加高效;通过继承实现对类的代理,使其无法代理final类或类中的final方法

为何调用代理类的方法,会自动进入InvocationHandlerinvoke()方法?

  • 通过newProxyInstance()创建代理类时,会为代理类设置InvocationHandlerh
  • 动态生成的代理类字节码,通过反编译可以发现,它实现了抽象主题中的每个方法
  • 方法的实现,是调用内部成员h.invoke()方法

this.h.invoke(this, method, args);

因此,调用代理类的方法时,实际上会调用InvocationHandlerinvoke()方法

到此这篇关于浅谈Java动态代理的实现的文章就介绍到这了,更多相关Java动态代理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 浅谈Java动态代理的实现

本文链接: https://lsjlt.com/news/126751.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 浅谈Java动态代理的实现
    目录一、代理设计模式1.1 什么是代理1.2 代理模式入门二、Java代理的三种实现2.1 静态代理2.2 Java自带的动态代理2.3 cglib实现动态代理三...
    99+
    2024-04-02
  • 浅谈Java注解和动态代理
    本文主要介绍Java中与注解和动态代理有关的部分知识,接下来我们看看具体内容。Annotation(注解)其实就是代码里的特殊标记, 它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉...
    99+
    2023-05-31
    java 注解 动态代理
  • 浅析Java中的动态代理
    目录代理常见功能代理模式的组成代理模式分类动态代理实现的技术JDK 代理的实现步骤CGLIB 代理实现步骤代理常见功能 日志代理 数据库访问的事务代理 代理模式的组成 抽象主题:通过...
    99+
    2024-04-02
  • 浅谈Node Inspector 代理实现
    背景 平时做 node 开发的时候,通过 node inspector 来进行断点调试是一个很常用的 debug 方式。但是有几个问题会导致我们的调试效率降低。 问题一:当使用 vscode 进行断点调试时...
    99+
    2022-06-04
    浅谈 Node Inspector
  • java动态代理实现代码
    目录1、代理模式2、动态代理3、原理研究4、应用5、总结1、代理模式 代理模式是常用的设计模式之一,也是开发中常见的设计模式。 简单的描述一下,代理模式就是将实现类隔离开,比如你想给...
    99+
    2024-04-02
  • 浅谈Java 代理机制
    目录一、常规编码方式二、代理模式概述三、静态代理3.1、什么是静态代理3.2、代码示例四、Java 字节码生成框架五、什么是动态代理六、JDK 动态代理机制6.1、使用步骤6.2、代...
    99+
    2024-04-02
  • Java实现动态代理的实例代码
    目录前言静态代理 动态代理 CGLib实现动态代理 总结前言 动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC...
    99+
    2024-04-02
  • java动态代理的实现方法
    这篇文章主要介绍“java动态代理的实现方法”,在日常操作中,相信很多人在java动态代理的实现方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java动态代理的实现方法”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-20
  • Java动态代理如何实现
    本篇内容介绍了“Java动态代理如何实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!JDK动态代理:利用反射机制生成一个实现代理接口的匿名...
    99+
    2023-06-30
  • 深入浅析java 1.8 中动态代理的原理
    这篇文章将为大家详细讲解有关深入浅析java 1.8 中动态代理的原理,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。JDK8动态代理源码分析动态代理的基本使用就不详细介绍了:例子:class...
    99+
    2023-05-31
    java 动态代理 ava
  • Java中怎么实现动态代理
    今天就跟大家聊聊有关Java中怎么实现动态代理,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、概述 代理是一种设计模式,其目的是为其他对象提供一个代理以控制对某个对象的访问,代理类...
    99+
    2023-06-17
  • java proxy动态代理怎么实现
    在Java中,可以通过使用`java.lang.reflect.Proxy`类来实现动态代理。首先,需要定义一个接口,代表被代理类和...
    99+
    2023-09-09
    java
  • Java如何实现JDK动态代理
    这篇文章主要讲解了“Java如何实现JDK动态代理”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java如何实现JDK动态代理”吧!概念代理:为控制A对象,而创建出新B对象,由B对象代替执行...
    99+
    2023-07-02
  • 深入浅析java 中JDK的动态代理类
    这期内容当中小编将会给大家带来有关深入浅析java 中JDK的动态代理类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。详解java JDK 动态代理类分析(java.lang.reflect.Proxy)...
    99+
    2023-05-31
    java jdk 动态代理类
  • 代理模式:JAVA静态代理和动态代理的实例和实现详解
    目录前言静态代理实现简述创建human接口创建接口实现类创建针对接口实现增强操作的代理代理实现效果动态代理实现简述要点:向上转型创建YoungMan接口创建两个接口实现类创建动态代理...
    99+
    2024-04-02
  • 使用Java怎么实现静态代理和动态代理
    本篇文章为大家展示了使用Java怎么实现静态代理和动态代理,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。代理模式代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问。主要解决:在直接...
    99+
    2023-06-14
  • java动态代理实现的原理是什么
    Java动态代理是指在运行时动态生成代理类的技术。它的实现原理主要涉及两个关键组件:接口和InvocationHandler。1. ...
    99+
    2023-09-09
    java
  • Java实现JDK动态代理的原理详解
    目录概念案例静态代理JDK动态代理模式原理分析真相大白概念 代理:为控制A对象,而创建出新B对象,由B对象代替执行A对象所有操作,称之为代理。一个代理体系建立涉及到3个参与角色:真实...
    99+
    2024-04-02
  • 浅谈用java实现事件驱动机制
    由于项目需求,需要为Java提供一套支持事件驱动机制的类库,可以实现类似于C#中的event和delegate机制。众所周知,Java语言本身以及其标准库中并没有提供事件驱动机制的相关接口,虽然Swing(我且认为其不属于标准库,因为一般没...
    99+
    2023-05-31
    java 事件处理机制 ava
  • Java 动态代理的多种实现方式
    目录一、动态代理简介二、动态代理的多种实现 1. 基于JDK的实现 2. 基于cglib的实现 三、为什么要有基于cglib的实现 四、两种方式的适用场景JDK动态代理 优点 缺点 ...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作