CGLib介绍

article/2025/10/31 13:48:20

1. CGLIB介绍

CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,

它可以在运行期扩展Java类与实现Java接口。CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP为他们提供方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类,性能比JDK强。

2. 为什么使用 CGLIB?

CGLIB是一个功能强大,高性能的代码生成它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。

3. 首先说一下JDK中的动态代理:

JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

4. 使用CGLib实现动态代理:

使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

5. 使用CGLib实现动态代理

实现一个业务类,注意,这个业务类并没有实现任何接口:   

package com.jpeony.spring.proxy.cglib;public class HelloService {public HelloService() {System.out.println("HelloService构造");}/*** 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的*/final public String sayOthers(String name) {System.out.println("HelloService:sayOthers>>"+name);return null;}public void sayHello() {System.out.println("HelloService:sayHello");}}

自定义MethodInterceptor:

package com.jpeony.spring.proxy.cglib;import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** 自定义MethodInterceptor*/
public class MyMethodInterceptor implements MethodInterceptor{/*** sub:cglib生成的代理对象* method:被代理对象方法* objects:方法入参* methodProxy: 代理方法*/@Overridepublic Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("======插入前置通知======");Object object = methodProxy.invokeSuper(sub, objects);System.out.println("======插入后者通知======");return object;}
}

生成CGLIB代理对象调用目标方法:

package com.jpeony.spring.proxy.cglib;import net.sf.cglib.core.DebuggingClassWriter;import net.sf.cglib.proxy.Enhancer;public class Client {public static void main(String[] args) {// 代理类class文件存入本地磁盘方便我们反编译查看源码System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");// 通过CGLIB动态代理获取代理对象的过程Enhancer enhancer = new Enhancer();// 设置enhancer对象的父类  即继承被代理类enhancer.setSuperclass(HelloService.class);// 设置enhancer的回调对象enhancer.setCallback(new MyMethodInterceptor());// 创建代理对象HelloService proxy= (HelloService)enhancer.create();// 通过代理对象调用目标方法proxy.sayHello();}}

Enhancer是一个字节码增强器,用来为非接口类型创建代理它的功能与java自带的Proxy类挺相似的它会根据某个给定的类创建子类并且所有非final的方法都带有回调函数。和Proxy不一样的是,不管是接口还是类他都能正常工作

运行结果:

======插入前置通知======
HelloService:sayHello
======插入后者通知======

6. CGLIB动态代理源码分析

实现CGLIB动态代理必须实现MethodInterceptor(方法拦截器)接口,源码如下:

package net.sf.cglib.proxy;public interface MethodInterceptor extends Callback{/*** All generated proxied methods call this method instead of the original method.* The original method may either be invoked by normal reflection using the Method object,* or by using the MethodProxy (faster).* @param obj "this", the enhanced object* @param method intercepted Method* @param args argument array; primitive types are wrapped* @param proxy used to invoke super (non-intercepted method); may be called* as many times as needed* @throws Throwable any exception may be thrown; if so, super method will not be invoked* @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.* @see MethodProxy*/    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;}

这个接口只有一个intercept()方法,这个方法有4个参数:

1)obj表示增强的对象,即实现这个接口类的一个对象;

2)method表示要被拦截的方法

3)args表示要被拦截方法的参数

4)proxy表示要触发父类的方法对象

在上面的Client代码中,通过Enhancer.create()方法创建代理对象,create()方法的源码:

 /*** Generate a new class if necessary and uses the specified* callbacks (if any) to create a new object instance.* Uses the no-arg constructor of the superclass.* @return a new instance*/public Object create() {classOnly = false;argumentTypes = null;return createHelper();}

该方法含义就是如果有必要就创建一个新类,并且用指定的回调对象创建一个新的对象实例,

使用的父类的参数的构造方法来实例化父类的部分。核心内容在createHelper()中,源码如下:

private Object createHelper() {preValidate();Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,ReflectUtils.getNames(interfaces),filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),callbackTypes,useFactory,interceptDuringConstruction,serialVersionUID);this.currentKey = key;Object result = super.create(key);return result;}

preValidate()方法校验callbackTypes、filter是否为空,以及为空时的处理。

通过newInstance()方法创建EnhancerKey对象,作为Enhancer父类AbstractClassGenerator.create()方法

创建代理对象的参数。

protected Object create(Object key) {try {ClassLoader loader = getClassLoader();Map<ClassLoader, ClassLoaderData> cache = CACHE;ClassLoaderData data = cache.get(loader);if (data == null) {synchronized (AbstractClassGenerator.class) {cache = CACHE;data = cache.get(loader);if (data == null) {Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);data = new ClassLoaderData(loader);newCache.put(loader, data);CACHE = newCache;}}}this.key = key;Object obj = data.get(this, getUseCache());if (obj instanceof Class) {return firstInstance((Class) obj);}return nextInstance(obj);} catch (RuntimeException e) {throw e;} catch (Error e) {throw e;} catch (Exception e) {throw new CodeGenerationException(e);}}

真正创建代理对象方法在nextInstance()方法中,该方法为抽象类AbstractClassGenerator的一个方法,签名如下:

abstract protected Object nextInstance(Object instance) throws Exception;

在子类Enhancer中实现,实现源码如下:

protected Object nextInstance(Object instance) {EnhancerFactoryData data = (EnhancerFactoryData) instance;if (classOnly) {return data.generatedClass;}Class[] argumentTypes = this.argumentTypes;Object[] arguments = this.arguments;if (argumentTypes == null) {argumentTypes = Constants.EMPTY_CLASS_ARRAY;arguments = null;}return data.newInstance(argumentTypes, arguments, callbacks);}

看看data.newInstance(argumentTypes, arguments, callbacks)方法,

第一个参数为代理对象的构成器类型,第二个为代理对象构造方法参数,第三个为对应回调对象。

最后根据这些参数,通过反射生成代理对象,源码如下:

 /*** Creates proxy instance for given argument types, and assigns the callbacks.* Ideally, for each proxy class, just one set of argument types should be used,* otherwise it would have to spend time on constructor lookup.* Technically, it is a re-implementation of {@link Enhancer#createUsingReflection(Class)},* with "cache {@link #setThreadCallbacks} and {@link #primaryConstructor}"** @see #createUsingReflection(Class)* @param argumentTypes constructor argument types* @param arguments constructor arguments* @param callbacks callbacks to set for the new instance* @return newly created proxy*/public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {setThreadCallbacks(callbacks);try {// Explicit reference equality is added here just in case Arrays.equals does not have oneif (primaryConstructorArgTypes == argumentTypes ||Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {// If we have relevant Constructor instance at hand, just call it// This skips "get constructors" machineryreturn ReflectUtils.newInstance(primaryConstructor, arguments);}// Take a slow path if observing unexpected argument typesreturn ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);} finally {// clear thread callbacks to allow them to be gc'dsetThreadCallbacks(null);}}

最后生成代理对象:

将其反编译后代码如下:

package com.jpeony.spring.proxy.cglib;import java.lang.reflect.Method;import net.sf.cglib.core.ReflectUtils;import net.sf.cglib.core.Signature;import net.sf.cglib.proxy.*;public class HelloService$$EnhancerByCGLIB$$4da4ebaf extends HelloServiceimplements Factory{private boolean CGLIB$BOUND;public static Object CGLIB$FACTORY_DATA;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback CGLIB$STATIC_CALLBACKS[];private MethodInterceptor CGLIB$CALLBACK_0; // 拦截器private static Object CGLIB$CALLBACK_FILTER;private static final Method CGLIB$sayHello$0$Method; // 被代理方法private static final MethodProxy CGLIB$sayHello$0$Proxy; // 代理方法private static final Object CGLIB$emptyArgs[];private static final Method CGLIB$equals$1$Method;private static final MethodProxy CGLIB$equals$1$Proxy;private static final Method CGLIB$toString$2$Method;private static final MethodProxy CGLIB$toString$2$Proxy;private static final Method CGLIB$hashCode$3$Method;private static final MethodProxy CGLIB$hashCode$3$Proxy;private static final Method CGLIB$clone$4$Method;private static final MethodProxy CGLIB$clone$4$Proxy;static void CGLIB$STATICHOOK1(){Method amethod[];Method amethod1[];CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];// 代理类Class class1 = Class.forName("com.jpeony.spring.proxy.cglib.HelloService$$EnhancerByCGLIB$$4da4ebaf");// 被代理类Class class2;amethod = ReflectUtils.findMethods(new String[] {"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (class2 = Class.forName("java.lang.Object")).getDeclaredMethods());Method[]  = amethod;CGLIB$equals$1$Method = amethod[0];CGLIB$equals$1$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");CGLIB$toString$2$Method = amethod[1];CGLIB$toString$2$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");CGLIB$hashCode$3$Method = amethod[2];CGLIB$hashCode$3$Proxy = MethodProxy.create(class2, class1, "()I", "hashCode", "CGLIB$hashCode$3");CGLIB$clone$4$Method = amethod[3];CGLIB$clone$4$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");amethod1 = ReflectUtils.findMethods(new String[] {"sayHello", "()V"}, (class2 = Class.forName("com.jpeony.spring.proxy.cglib.HelloService")).getDeclaredMethods());Method[] 1 = amethod1;CGLIB$sayHello$0$Method = amethod1[0];CGLIB$sayHello$0$Proxy = MethodProxy.create(class2, class1, "()V", "sayHello", "CGLIB$sayHello$0");}final void CGLIB$sayHello$0(){super.sayHello();}public final void sayHello(){MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if(this.CGLIB$CALLBACK_0 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if(var10000 != null) {// 调用拦截器var10000.intercept(this, CGLIB$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy);} else {super.sayHello();}}............}

首先呢,动态代理类继承了我们的业务类,所以如果业务类是final修饰的那么则不能被代理同理final修饰的方法也是不能被代理的,动态代理类根据我们业务类的每一个方法都生成了2个代理方法,第一个代理方法直接调用父类的方法,也就是我们业务类的方法,第二个方法也就是代理类真正调用的方法是经过封装的他会去判断是否实现了MethodInterceptor 的intercept方法,如果实现了则会调用intercept方法。这个MethodInterceptor 就是我们在enhancer.setCallback(new UserMethodInterceptor())这里设置进去的。从而完成了由代理对象访问到目标对象的动态代理实现。

 

JDK动态代理实现原理(jdk8):https://blog.csdn.net/yhl_jxy/article/details/80586785

转载自: https://blog.csdn.net/yhl_jxy/article/details/80633194


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

相关文章

cglib的简单使用

一、前言 最近在看Spring的源码&#xff0c;其中有牵扯到cglib的相关内容&#xff0c;遂简单记录下cglib是如何使用的 二、原理&#xff08;节选自网络&#xff09; CGLIB原理&#xff1a;动态生成一个要代理类的子类&#xff0c;子类重写要代理的类的所有不是final的方法。…

cglib动态代理

前面介绍了代理模式和JAVA动态代理&#xff0c;这片文章主要解析cglib动态代理实现。 基本介绍 CGLIB&#xff08;Code Generation Library&#xff09;&#xff0c;是一个强大的&#xff0c;高性能&#xff0c;高质量的 Code 生成类库&#xff0c;它可以在运行期扩展 Java 类…

动态代理之 cglib 实现

&#xff08;尊重劳动成果&#xff0c;转载请注明出处&#xff1a;https://blog.csdn.net/qq_25827845/article/details/87513102冷血之心的博客&#xff09; 目录 前言&#xff1a; 正文&#xff1a; AOP&#xff08;面向切面编程&#xff09; JDK动态代理 cglib实现动态…

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)。…