动态代理之 cglib 实现

article/2025/10/31 16:02:57

(尊重劳动成果,转载请注明出处:https://blog.csdn.net/qq_25827845/article/details/87513102冷血之心的博客)

目录

前言:

正文:

AOP(面向切面编程)

JDK动态代理

cglib实现动态代理

cglib包结构:

cglib动态代理相关的基础类:

cglib动态代理Demo

总结:


前言:

        这篇文章主要介绍使用cglib来实现动态代理,也就是Spring中AOP(面向切面编程)的动态代理的底层实现之一。之前在学生时代对动态代理和AOP都进行过一些总结,详情请见:Java设计模式—代理模式 以及 Spring核心AOP(面向切面编程)总结 。但是随着工作中实际遇到的问题,这些文章感觉都比较肤浅,仅仅是一些简单的概念介绍,对于使用cglib来实现的动态代理甚至都没有一个Demo来阐述和介绍。基于此,本篇博客讲述如何使用cglib来实现动态代理,并且介绍cglib中的常用基础类。

正文:

AOP(面向切面编程)

(1)面向切面编程,指扩展功能不修改源代码,将功能代码从业务逻辑代码中分离出来。

  • 主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等等。
  • 主要意图:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

(2)AOP底层使用动态代理实现。包括两种方式:

  • 使用JDK动态代理实现。
  • 使用cglib来实现

由AOP的概念我们将AOP视为一种横向的拦截器,也就是在执行业务逻辑之前和之后都可以进行一些额外的操作。

JDK动态代理

       JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理。原理是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。关于JDK动态代理的介绍请参考我的博文:Java设计模式—代理模式

cglib实现动态代理

      cglib实现动态代理是我们本篇文章的重点阐述内容。CGLIB是一个强大、高性能的字节码生成库。使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类

cglib包结构:

  • net.sf.cglib.core    底层字节码处理类。
  • net.sf.cglib.transform    该包中的类用于class文件运行时转换或编译时转换。
  • net.sf.cglib.proxy    该包中的类用于创建代理和方法拦截。
  • net.sf.cglib.reflect    该包中的类用于快速反射,并提供了C#风格的委托。
  • net.sf.cglib.util    集合排序工具类。
  • net.sf.cglib.beans    JavaBean工具类。

cglib动态代理相关的基础类:

  • net.sf.cglib.proxy.Enhancer    主要的增强类。
  • net.sf.cglib.proxy.MethodInterceptor    主要的方法拦截类,它是Callback接口的子接口,需要用户实现。
  • net.sf.cglib.proxy.MethodProxy    JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。

       cglib是通过动态的生成一个子类去覆盖所要代理类的非final方法,并设置好callback,则原有类的每个方法调用就会转变成调用用户定义的拦截方法(intercept)。常用API如下:

(1) net.sf.cglib.proxy.Callback接口在CGLIB包中是一个重要的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。

(2) net.sf.cglib.proxy.MethodInterceptor能够满足任何的拦截(interception )需要。对有些情况下可能过度。为了简化和提高性能,CGLIB包提供了一些专门的回调(callback)类型

  • net.sf.cglib.proxy.FixedValue 为提高性能,FixedValue回调对强制某一特别方法返回固定值是有用的。
  • net.sf.cglib.proxy.NoOp NoOp回调把对方法调用直接委派到这个方法在父类中的实现。
  • net.sf.cglib.proxy.LazyLoader 当实际的对象需要延迟装载时,可以使用LazyLoader回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用。
  • net.sf.cglib.proxy.Dispatcher Dispathcer回调和LazyLoader回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用。
  • net.sf.cglib.proxy.ProxyRefDispatcher ProxyRefDispatcher回调和Dispatcher一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递。

cglib动态代理Demo

(1)创建一个普通类做为我们的被代理类

// 创建一个普通类做为代理类
class Person {//  代理类中由普通方法public void eat() {System.out.println("我要开始吃饭咯...");}public void play() {System.out.println("我要出去玩耍了,,,");}
}

(2)创建一个拦截器MyApiInterceptor,实现MethodInterceptor

class MyApiInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("吃饭前我会先洗手"); // 此处可以做一些操作Object result = proxy.invokeSuper(obj, args);System.out.println("吃完饭我会先休息会儿" );  // 方法调用之后也可以进行一些操作return result;}
}

Object result=proxy.invokeSuper(o,args); 表示调用原始类的被拦截到的方法。这个方法的前后添加需要的过程。在这个方法中,我们可以在调用原方法之前或之后注入自己的代码。 

由于性能的原因,对原始方法的调用使用CGLIB的net.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用java.lang.reflect.Method对象。

(3) 创建类加强器Enhancer来生成代理对象

public class TestCglib {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Person.class);enhancer.setCallback(new MyApiInterceptor());Person person = (Person) enhancer.create();person.eat();}
}

net.sf.cglib.proxy.Enhancer中有几个常用的方法:

  • void setSuperclass(java.lang.Class superclass) 设置产生的代理对象的父类。
  • void setCallback(Callback callback) 设置CallBack接口的实例。
  • void setCallbacks(Callback[] callbacks) 设置多个CallBack接口的实例。
  • void setCallbackFilter(CallbackFilter filter) 设置方法回调过滤器。
  • Object create() 使用默认无参数的构造函数创建目标对象。
  • Object create(Class[], Object[]) 使用有参数的构造函数创建目标对象。参数Class[] 定义了参数的类型,第二个Object[]是参数的值。

(4)结果输出如下:

好了,至此,我们完成了一个简单的cglib实现动态代理。但是看起来还不够,比如说,我只想在吃饭这个api的前后进行一些额外的操作(比如洗手和休息),但是玩耍前后不需要增加额外的操作,那么怎么实现呢?

答案:

CallbackFilter可以实现不同的方法使用不同的回调方法。CallbackFilter中的accept方法, 根据不同的method返回不同的值i, 这个值是在callbacks中的顺序, 就是调用了callbacks[i]

我们的完整代码如下:

package com.pak;import net.sf.cglib.proxy.*;import java.lang.reflect.Method;public class TestCglib {public static void main(String[] args) {// 定义一个回调接口的数组Callback[] callbacks = new Callback[] {new MyApiInterceptor(), new MyApiInterceptorForPlay()};Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Person.class); // 设置要代理的父类enhancer.setCallbacks(callbacks); // 设置回调的拦截器数组enhancer.setCallbackFilter(new CallbackFilterImpl()); // 设置回调选择器Person person = (Person) enhancer.create(); // 创建代理对象person.eat();System.out.println("--------------------");person.play();}
}class MyApiInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("吃饭前我会先洗手"); // 此处可以做一些操作Object result = proxy.invokeSuper(obj, args);System.out.println("吃完饭我会先休息会儿" );  // 方法调用之后也可以进行一些操作return result;}
}
class MyApiInterceptorForPlay implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("出去玩我会先带好玩具"); // 此处可以做一些操作Object result = proxy.invokeSuper(obj, args);System.out.println("玩一个小时我就回家了" );  // 方法调用之后也可以进行一些操作return result;}
}class CallbackFilterImpl implements CallbackFilter {@Overridepublic int accept(Method method) {if (method.getName().equals("play"))return 1;elsereturn 0;}
}// 创建一个普通类做为代理类
class Person {//  代理类中由普通方法public void eat() {System.out.println("我要开始吃饭咯...");}public void play() {System.out.println("我要出去玩耍了,,,");}
}

输出结果如下:

当然,在我们的回调数组中也可以使用cglib已经给我们提供的回调接口,如下所示:

  • FixedValue
  • NoOp
  • LazyLoader
  • Dispatcher
  • ProxyRefDispatcher

再来说一个问题吧,我们的演示项目是一个Maven项目,所需要引入的依赖为cglib,如下所示:

<dependencies><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.2.9</version></dependency>
</dependencies>

做为一名Java开发人员,我相信大家都正在或者将要学习Maven,如果大家有学习需求,可以参考我的Maven系列博文:

Maven基础概念和安装配置教程

Maven的仓库和settings.xml配置文件

Maven的坐标与依赖

Maven的生命周期和插件

Maven的聚合与继承

 

总结:

本文我们抛砖引玉给大家介绍了cglib实现动态代理的基础概念和基本Demo,回到Spring上来说,Spring选择的动态代理的方式如下:

  1. 如果目标对象实现了接口,默认情况下回采用JDK的动态代理实现AOP,也可以强制使用cglib实现AOP
  2. 如果目标对象没有实现接口,必须采用cglib库,Spring会自动在JDK动态代理和cglib之间转换

由于cglib实现动态代理方面的介绍已经有很多,所以本博客的总结站在了巨人的肩膀上。参考文献如下:

https://www.cnblogs.com/icejoywoo/archive/2011/06/05/2072970.html

https://www.cnblogs.com/shijiaqi1066/p/3429691.html  

 

如果对你有帮助,记得点赞哦~欢迎大家关注我的博客,我会持续更新,如果有什么问题,可以进群366533258一起交流学习哦~

 


http://chatgpt.dhexx.cn/article/ElFutCxc.shtml

相关文章

CGLIB介绍与原理

一、什么是 CGLIB? CGLIB是一个功能强大&#xff0c;高性能的代码生成包。它为没有实现接口的类提供代理&#xff0c;为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理&#xff0c;但当要代理的类没有实现接口或者为了更好的性能&#xff0c;CGLIB是一个…

CGLIB(Code Generation Library)详解

什么是CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架&#xff08;Spring、dynaop&#xff09;中&#xff0c;用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架&#xff0c;同样使用CGLIB来代理单端&#xff08;多对一和一对一&#xff09;…

【动态代理】CGLIB 动态代理的使用及原理

1. CGLIB 动态代理介绍 什么是 CGLIB&#xff1f; CGLIB是一个功能强大&#xff0c;高性能的代码生成包。它为没有实现接口的类提供代理&#xff0c;为JDK的动态代理提供了很好的补充。 通常可以使用Java的动态代理创建代理&#xff0c;但当要代理的类没有实现接口或者为了更…

CGLIB详解(最详细)

转载地址:https://blog.csdn.net/danchu/article/details/70238002 什么是CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架&#xff08;Spring、dynaop&#xff09;中&#xff0c;用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架&#xff0c…

GPG error解决方案

问题: sudo apt-get update时报错GPG error 解决方案: // F42ED6FBAB17C654是根据你报错那一行确定的 sudo gpg --keyserver keyserver.ubuntu.com --recv F42ED6FBAB17C654 sudo gpg --export --armor F42ED6FBAB17C654 | sudo apt-key add -然后: sudo apt-get update

GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

阿里云CentOS 7.9 64位 搭建网站踩坑实录 问题1.GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql Failing package is: mysql-community-libs-compat-5.7.37-1.el7.x86_64 GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql …

gpg文件加密解密

使用Ubuntu对文件进行gpg格式的加密和解密 安装 下载源码安装  ./configure   make   make install 命令安装 Debian环境 sudo apt-get install gnupg Fedora 环境 yum install gnupg 加密 gpg -c abc.txt 会让输入一个加密的密码&#xff0c;需要自己输入 解密 gpg -o …

GPG(GnuPG)的安装和使用

基于网络的开源项目&#xff0c;能给用户带来在公共标准基础上的自由发挥&#xff0c;并且能很好地给每个自愿人士提供了共享贡献的机会。但是&#xff0c;同时也因为大众化给使用共享的程序员或团队带来了安全性问题。 当程序员从中央仓库下载第三方构件的时候&#xff0c;下载…

gpg使用

https://blog.csdn.net/weixin_42559321/article/details/82147888 https://www.cnblogs.com/wanghongli/archive/2018/01/08/8241809.html rpm2cpio *.rpm | cpio -imd       #解压一个rpm包rpm -ivh *.rpm --force       #强制安装这个rpm包rpm -iv…

如何在Git中使用GPG

开篇之前&#xff0c;先给大伙看点东西 是不是很想要&#xff1f;你找对地方了! 下面是教程&#xff1a; 在 “开始”菜单 打开Git Bash 输入 gpg --gen-key 显示如下 $ gpg --gen-keygpg (GnuPG) 2.2.13-unknown; Copyright (C) 2019 Free Software Foundation, Inc.This …

成功解决gpg: 找不到有效的 OpenPGP 数据

在Ubuntu系统上安装docker时出现gpg: 找不到有效的 OpenPGP 数据的报错 解决方案&#xff1a; wget https://download.docker.com/linux/ubuntu/gpg sudo apt-key add gpg随后再次执行下载指令&#xff0c;解决报错 成功解决gpg: 找不到有效的 OpenPGP 数据的报错 欢迎小伙…

GPG Overview

Overview PGP目前支持的算法 非对称算法: RSA, ELG, DSA, ECDH, ECDSA, EDDSA对称算法: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256哈希算法: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224压缩算法: Uncompres…

GPG 使用初步

GPG 使用初步 1. PGP 软件的安装 PGP 的版本有很多&#xff0c;但由于其商业软件的特性&#xff0c;不能自由使用&#xff0c;自由软件基金会决定开发一个 PGP 的替代品&#xff0c;取名为 GnuPG &#xff0c;这就是 PGP 的由来   GPG 是基于命令行的程序&#xff0c;主要面…

gpg加解密软件学习

为什么要学习gpg呢&#xff1f;因为要在Linux下把一个邮箱的密码加密&#xff0c;不让其他人看到该邮箱真正的密码。 为了不让其他人看到真正的邮箱密码&#xff0c;我们需要对其进行加密。 加密的方式是先把密码先写到一个文件A中&#xff0c;然后使用相关的加密软件对该文件…

java动态代理

java动态代理实现与原理详细分析 原文地址 关于Java中的动态代理&#xff0c;我们首先需要了解的是一种常用的设计模式--代理模式&#xff0c;而对于代理&#xff0c;根据创建代理类的时间点&#xff0c;又可以分为静态代理和动态代理。 一、代理模式 代理模式是常用的java…

动态规划 --- 算法思想介绍

一.动态规划的基本概念 动态规划在五种算法设计方法中难度最大&#xff0c;它建立在最优原则的基础上.采用动态规划方法&#xff0c;可以高效地解决许多用贪婪算法或分治法无法解决的问题.动态规划(dynamic programming)属运筹学中的规划论分支&#xff0c;是求解决策过程最优…

动态规划算法详解

动态规划算法通常用于求解具有最优性质的问题 基本概念 动态规划过程是&#xff1a;每次决策依赖于当前状态&#xff0c;又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的&#xff0c;所以&#xff0c;这种多阶段最优化决策解决问题的过程就称为动态规划(DP)。…

动态规划原理

1. 基本概念 动态规划通过拆分问题&#xff0c;将问题拆分成许多的子问题&#xff0c;定义问题状态和状态之间的关系&#xff08;即状态转移方程或递推公式&#xff09;&#xff0c;使得问题能够以递推&#xff08;或者说分治&#xff09;的方式去解决。按顺序求解子问题&…

动态代理详解

想要更加透彻的理解动态代理&#xff0c;首先要熟悉下静态代理 一、静态代理 总结来说&#xff1a;目标类和代理类实现了相同的接口&#xff0c;在代理类中依赖了目标类&#xff0c;代理类的方法中调用了目标类的方法&#xff0c;并做了一些增强性的工作。 1、实现静态代理&…

CAD动态块制作

CAD动态块制作 拉伸动态块柜体A拉伸动态块制作第一步&#xff1a;制作柜体A第二步&#xff1a;进入块编辑器编辑第三步&#xff1a;关闭块编辑器 柜体B拉伸动态块制作第一步&#xff1a;制作柜体B第二步&#xff1a;进入块编辑器第三步&#xff1a;关闭块编辑器 可见性动态块第…