首页 > 其他分享 >JDK动态代理和CGLIB动态代理

JDK动态代理和CGLIB动态代理

时间:2024-06-21 15:34:35浏览次数:11  
标签:调用 JDK 代理 CGLIB 动态 方法 public

Java动态代理是一种在运行时创建代理对象的技术,它允许开发者在不修改目标类代码的情况下,通过代理类对目标类的实例方法进行增强或拦截。动态代理的核心价值在于能够在程序运行阶段动态地生成一个实现了预定义接口的新类,这个新类就是所谓的“代理类”。

在Java中,有两种主要的实现方式:

  1. JDK动态代理

    • JDK动态代理是Java SE API内置的一种动态代理机制,它通过java.lang.reflect.Proxy类和InvocationHandler接口来实现。
    • 使用JDK动态代理,目标类必须实现至少一个接口。代理类会继承Proxy类并实现与目标类相同的接口,这样代理类就能替代目标类成为接口的实现者。
    • 当调用代理类的方法时,实际会调用到InvocationHandler接口中的invoke方法,在invoke方法内部可以添加额外的功能,如方法执行前后的附加逻辑、权限控制、日志记录等,然后调用目标对象的实际方法。
  2. CGLIB动态代理

    • CGLIB(Code Generation Library)是一个第三方库,它通过字节码技术为没有实现接口的目标类生成子类作为代理类。
    • CGLIB代理能够代理任何未实现接口的类,因为它是通过继承的方式生成一个目标类的子类,重写父类的方法并在方法中加入增强逻辑。
    • 这种方式更加灵活,但要求代理的目标类不能声明为final类,并且方法也不能是final方法,否则无法被CGLIB成功继承和重写。

无论是哪种动态代理方式,其目的都是为了在目标方法执行前后增加额外的行为,或者改变原有的行为,以满足特定的需求,例如AOP(面向切面编程)框架中的事务管理、性能监控、日志记录等功能。

1.JDK动态代理

以下是一个简单的JDK动态代理示例,假设我们有一个接口Sellable和它的实现类RealEstate。在这个例子中,我们将创建一个动态代理来记录每次调用卖房方法时的日志信息。

首先,定义业务接口:

// 业务接口:买卖物品
public interface Sellable {
    void sell(String item);
    void buy(String item);
}

然后,实现这个接口的实体类:

// 接口的实现类:房地产公司
public class RealEstate implements Sellable {
    @Override
    public void sell(String item) {
        System.out.println("实际销售房源: " + item);
    }

    @Override
    public void buy(String item) {
        System.out.println("实际购买房源: " + item);
    }
}

接下来,创建一个InvocationHandler实现类,用于处理对代理对象方法的调用:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LoggingInvocationHandler implements InvocationHandler {
    // 被代理的对象引用
    private final Sellable target;

    public LoggingInvocationHandler(Sellable target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法调用前的操作:记录日志
        System.out.println("开始销售房源操作...");
        
        // 调用目标对象的方法,并返回结果
        Object result = method.invoke(target, args);

        // 方法调用后的操作:再次记录日志
        System.out.println("完成销售房源操作.");

        return result;
    }
}

最后,通过Proxy类创建并使用动态代理:

public class Main {
    public static void main(String[] args) {
        // 实例化真实对象
        Sellable realEstate = new RealEstate();

        // 创建代理对象,并将真实对象传给InvocationHandler
        // 这块代码是动态代理的精髓
        Sellable proxy = (Sellable) Proxy.newProxyInstance(
                Sellable.class.getClassLoader(),
                new Class<?>[]{Sellable.class},
                new LoggingInvocationHandler(realEstate)
        );

        // 现在调用的是代理对象的方法,但会触发InvocationHandler的逻辑
        proxy.sell("豪华别墅");
        proxy.buy("大平层");

        // 输出:
        // 开始销售房源操作...
        // 实际销售房源: 豪华别墅
        // 完成销售房源操作.
    }
}

在这个例子中,当客户端代码通过代理对象调用sell方法时,实际上会执行LoggingInvocationHandler中的invoke方法,在该方法内部先进行日志记录,然后调用实际对象的方法完成销售动作,最后再记录一次日志。这就是JDK动态代理的基本应用。

Q:为什么要搞这么麻烦呢, 直接写一个方法, 把实际对象调用实际方法写到这个方法里不是跟简单吗?

这种简化方式确实可以在某些场景下直接满足需求,例如在业务逻辑相对简单、需要增强的功能点不多的情况下,其实这种方式就是静态代理。但是动态代理技术的设计初衷和优势在于:

  1. 解耦:通过动态代理,我们可以将功能增强(如日志记录、事务管理、权限检查等)的代码与业务逻辑分离,使得业务类更专注于业务本身,而不需要关心额外的横切关注点。
  2. 灵活扩展:如果在系统中有很多类似的方法都需要添加同样的增强处理,使用动态代理可以避免大量重复代码的编写。只需要定义一个InvocationHandler实现类,就可以对所有实现了同一接口的对象进行统一的增强处理。
  3. 运行时动态决定行为:动态代理是在运行时动态生成代理对象,这意味着代理对象的行为可以根据运行时条件来决定,比如根据配置信息动态开启或关闭日志记录、性能监控等功能。
  4. AOP支持:在面向切面编程(Aspect Oriented Programming, AOP)框架中,动态代理是实现切面织入的重要手段。通过动态代理,可以在不侵入原有业务代码的前提下,方便地实现如事务管理、异常处理、性能统计等横切关注点的统一管理。

因此,虽然表面上看动态代理可能会显得比直接调用方法更为复杂,但在实际项目开发中,它为解决特定问题提供了强大且灵活的支持,尤其在大型、复杂的软件系统中具有很高的价值。

2.CGLIB动态代理

CGLIB(Code Generation Library)是一个强大的高性能的代码生成库,它广泛应用于Java的动态代理实现中,特别是在Spring AOP框架中作为JDK动态代理的一种补充或替代方案。CGLIB通过字节码技术(Bytecode Engineering Library, BCEL 或者 ASM 库)在运行时对目标类生成一个子类,并覆盖其中非final和非private的方法来创建代理对象。

在Spring框架中,当配置了proxy-target-class属性为true时,Spring会自动选择CGLIB作为代理机制来为目标类创建代理实例。

以下是一个使用CGLIB库进行动态代理的简单示例,假设我们有一个Calculator类,现在希望通过CGLib创建一个代理类来增强其方法调用:

首先,定义原始的业务类:

public class Calculator {
    public int add(int i, int j) {
        System.out.println("Executing original add method.");
        return i + j;
    }
}

然后,实现CGLIB的MethodInterceptor接口以提供增强逻辑:

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class LoggingInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 在方法调用前打印日志
        System.out.println("Before calling " + method.getName() + " with arguments: " + Arrays.toString(args));

        // 调用实际方法并获取结果
        Object result = proxy.invokeSuper(obj, args);

        // 在方法调用后打印日志
        System.out.println("After calling " + method.getName() + ", result is: " + result);

        return result;
    }
}

接下来,使用CGLIB的Enhancer类生成代理对象:

import net.sf.cglib.core.NamingPolicy;
import net.sf.cglib.core.DefaultNamingPolicy;
import net.sf.cglib.proxy.Enhancer;

public class CglibDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Calculator.class); // 设置要代理的目标类

        // 设置拦截器(MethodInterceptor)
        enhancer.setCallback(new LoggingInterceptor());

        // 可选:设置命名策略,避免代理类名称冲突
        enhancer.setNamingPolicy(DefaultNamingPolicy.INSTANCE);

        // 创建并获取代理对象
        Calculator calculatorProxy = (Calculator) enhancer.create();

        // 通过代理对象调用方法
        int sum = calculatorProxy.add(3, 5);
        System.out.println("Sum is: " + sum);
    }
}

当运行这段代码时,CGLib会动态地为Calculator类生成一个子类作为代理,并在调用add方法前后执行LoggingInterceptor中的intercept方法。因此,输出将包含日志信息以及原方法的结果。

标签:调用,JDK,代理,CGLIB,动态,方法,public
From: https://www.cnblogs.com/GilbertDu/p/18245948

相关文章

  • Nginx 反向代理 (泛域名->泛域名,https,静态文件)
    Nginx反向代理配置指南(泛域名->泛域名,HTTPS,静态文件)完整版server{#监听80端口listen80;listen443sslhttp2;; #...... #泛域名server_name*.{fromName}.com;#获取"*"参数set$subdm'';if($host~*"(.*......
  • 【鸿蒙实战教程】HarmonyOS中的动态import和静态import的概念与区别
    什么是import鸿蒙核心语言是ArkTs,基于TS的扩展,所以里边少不了JS引擎的影子。    对于鸿蒙而言,我们在文件中引入其他文件时,会涉及到两个概念,静态import和动态import。    什么是引入其他文件呢?例如:import{TestA}from'./TestA'import{hilog}from'@kit......
  • DNS透明代理
    一、实验思路及拓扑1、实验思路外网连接两个ISP分别为R2和R3,并分别提供两个DNS1和DNS2,对应到相同的域名www.quw.com(地址分别对应155.1.2.10和136.1.2.10)对于内网用户通过防火墙IPS路由、智能选路带宽分担及透明DNS功能由内网向外网访问域名www.quw.com,由防火墙根据带宽选择......
  • HUSKY:一个优化大语言模型多步推理的新代理框架
    推理被高度认可为生成人工智能的下一个前沿领域。通过推理,我们可以将任务分解为更小的子集并单独解决这些子集。例如以前的论文:思维链、思维树、思维骨架和反射,都是最近解决LLM推理能力的一些技术。此外推理还涉及一些外围功能,例如访问外部数据或工具。在最近的几年里,我们已经看到......
  • Python 学习 第三册 第13章 动态规划
    ----用教授的方式学习目录13.1 又见斐波那契数列13.2 动态规划与 0/1 背包问题13.3 动态规划与分治算法13.1 又见斐波那契数列一个很直观的斐波那契数列的递归实现:deffib(n):    """假设n是非负整数返回第n个斐波那契数"""    ifn==0o......
  • windows/linux下JDK安装配置教程
    JDK安装配置教程前言一、jdk下载1.本人资源下载2.官网下载2.1进入java下载页2.2选择版本并下载二、windows1.解压版(最简单、快捷)1.1版本信息1.2解压1.3完成2.安装版2.1版本信息2.2开始安装2.3安装完成三、Linux1.解压版(最简单、快捷)1.1版本信息1.2......
  • 重学java 79.JDK新特性 ⑤ JDK8之后的新特性
    别怕失败,大不了重头再来                          ——24.6.20一、接口的私有方法Java8版本接口增加了两类成员:        公共的默认方法        公共的静态方法Java9版本接口又新增了一类成员:......
  • Ant-Design-Vue动态表头并填充数据(含示例代码)
    关注我,持续分享逻辑思维&管理思维&面试题;可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;推荐专栏《10天学会使用asp.net编程AI大模型》,目前已完成所有内容。一顿烧烤不到的费用,让人能紧跟时代的浪潮。从普通网站,到公众号、小程序,再到AI大模型网站。干货满满。学成后可......
  • [AHK2] 借助vlc设置桌面动态背景
    概述网上可以找到许多设置桌面动态背景的c++代码,我们将它用ahk实现即可。我已经使用了很长一段时间,体验十分不错,所以推荐给大家。完整脚本请看最后一节。发送消息SendMsgToProgman(){DllCall('SendMessageTimeout','ptr',WinGetID('ahk_classProgman'),'......
  • 【攻防技术系列】动态库和静态库有什么区别?
    计算机的运行当然离不开内存。程序运行在内存当中,那么程序在内存中的布局是什么样子的呢?程序的内存分为代码区、数据区、堆区和栈区,它们的布局是这样的,这里重点看代码区。代码区中是什么呢?这里主要就是你写的代码,当然还有你使用的库。这里主要是标准库,以及非标准库,也就是......