Spring AOP实现原理详解之Cglib代理实现

article/2025/10/3 12:50:53

引入

我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理。

要了解动态代理是如何工作的,首先需要了解

  • 什么是代理模式?
  • 什么是动态代理?
  • 什么是Cglib?
  • SpringAOP和Cglib是什么关系?

动态代理要解决什么问题?

什么是代理?

代理模式(Proxy pattern): 为另一个对象提供一个替身或占位符以控制对这个对象的访问

举个简单的例子:

我(client)如果要买(doOperation)房,可以找中介(proxy)买房,中介直接和卖方(target)买房。中介和卖方都实现买卖(doOperation)的操作。中介就是代理(proxy)。

什么是动态代理?

动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。

在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。

什么是Cglib? SpringAOP和Cglib是什么关系?

Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。

  • 最顶层是字节码,字节码相关的知识请参考 JVM基础 - 类字节码详解
  • ASM是操作字节码的工具
  • cglib基于ASM字节码工具操作字节码(即动态生成代理,对方法进行增强)
  • SpringAOP基于cglib进行封装,实现cglib方式的动态代理

Cglib代理的案例

这里我们写一个使用cglib的简单例子。@pdai

pom包依赖

引入cglib的依赖包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>tech-pdai-spring-demos</artifactId><groupId>tech.pdai</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>007-spring-framework-demo-aop-proxy-cglib</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><!-- https://mvnrepository.com/artifact/cglib/cglib --><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency></dependencies></project>

定义实体

User

package tech.pdai.springframework.entity;/*** @author pdai*/
public class User {/*** user's name.*/private String name;/*** user's age.*/private int age;/*** init.** @param name name* @param age  age*/public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}

被代理的类

即目标类, 对被代理的类中的方法进行增强

package tech.pdai.springframework.service;import java.util.Collections;
import java.util.List;import tech.pdai.springframework.entity.User;/*** @author pdai*/
public class UserServiceImpl {/*** find user list.** @return user list*/public List<User> findUserList() {return Collections.singletonList(new User("pdai", 18));}/*** add user*/public void addUser() {// do something}}

cglib代理

cglib代理类,需要实现MethodInterceptor接口,并指定代理目标类target

package tech.pdai.springframework.proxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;/*** This class is for proxy demo.** @author pdai*/
public class UserLogProxy implements MethodInterceptor {/*** 业务类对象,供代理方法中进行真正的业务方法调用*/private Object target;public Object getUserLogProxy(Object target) {//给业务对象赋值this.target = target;//创建加强器,用来创建动态代理类Enhancer enhancer = new Enhancer();//为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)enhancer.setSuperclass(this.target.getClass());//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦enhancer.setCallback(this);// 创建动态代理类对象并返回return enhancer.create();}// 实现回调方法@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// log - before methodSystem.out.println("[before] execute method: " + method.getName());// call methodObject result = proxy.invokeSuper(obj, args);// log - after methodSystem.out.println("[after] execute method: " + method.getName() + ", return value: " + result);return null;}
}

使用代理

启动类中指定代理目标并执行。

package tech.pdai.springframework;import tech.pdai.springframework.proxy.UserLogProxy;
import tech.pdai.springframework.service.UserServiceImpl;/*** Cglib proxy demo.** @author pdai*/
public class ProxyDemo {/*** main interface.** @param args args*/public static void main(String[] args) {// proxyUserServiceImpl userService = (UserServiceImpl) new UserLogProxy().getUserLogProxy(new UserServiceImpl());// call methodsuserService.findUserList();userService.addUser();}
}

简单测试

我们启动上述类main 函数,执行的结果如下:

[before] execute method: findUserList
[after] execute method: findUserList, return value: [User{name='pdai', age=18}]
[before] execute method: addUser
[after] execute method: addUser, return value: null

Cglib代理的流程

我们把上述Demo的主要流程画出来,你便能很快理解

更多细节:

  • 在上图中,我们可以通过在Enhancer中配置更多的参数来控制代理的行为,比如如果只希望增强这个类中的一个方法(而不是所有方法),那就增加callbackFilter来对目标类中方法进行过滤;Enhancer可以有更多的参数类配置其行为,不过我们在学习上述主要的流程就够了。
  • final方法为什么不能被代理?很显然final方法没法被子类覆盖,当然不能代理了。
  • Mockito为什么不能mock静态方法?因为mockito也是基于cglib动态代理来实现的,static方法也不能被子类覆盖,所以显然不能mock。但PowerMock可以mock静态方法,因为它直接在bytecode上工作,更多可以看 Mockito单元测试 。(pdai: 通了没?是不是so easy...)

SpringAOP中Cglib代理的实现

SpringAOP封装了cglib,通过其进行动态代理的创建。

我们看下CglibAopProxy的getProxy方法

@Override
public Object getProxy() {return getProxy(null);
}@Override
public Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());}try {Class<?> rootClass = this.advised.getTargetClass();Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");// 上面流程图中的目标类Class<?> proxySuperClass = rootClass;if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {proxySuperClass = rootClass.getSuperclass();Class<?>[] additionalInterfaces = rootClass.getInterfaces();for (Class<?> additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface);}}// Validate the class, writing log messages as necessary.validateClassIfNecessary(proxySuperClass, classLoader);// 重点看这里,就是上图的enhancer,设置各种参数来构建Enhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader &&((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}enhancer.setSuperclass(proxySuperClass);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));// 设置callback回调接口,即方法的增强点Callback[] callbacks = getCallbacks(rootClass);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}// 上节说到的filterenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);// 重点:创建proxy和其实例return createProxyClassAndInstance(enhancer, callbacks);}catch (CodeGenerationException | IllegalArgumentException ex) {throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +": Common causes of this problem include using a final class or a non-visible class",ex);}catch (Throwable ex) {// TargetSource.getTarget() failedthrow new AopConfigException("Unexpected AOP exception", ex);}
}

获取callback的方法如下,提几个理解的要点吧,具体读者在学习的时候建议把我的例子跑一下,然后打一个断点进行理解。

rootClass
advised
exposeProxy
DynamicAdvisedInterceptor
targetInterceptor
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {// Parameters used for optimization choices...boolean exposeProxy = this.advised.isExposeProxy();boolean isFrozen = this.advised.isFrozen();boolean isStatic = this.advised.getTargetSource().isStatic();// Choose an "aop" interceptor (used for AOP calls).Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);// Choose a "straight to target" interceptor. (used for calls that are// unadvised but can return this). May be required to expose the proxy.Callback targetInterceptor;if (exposeProxy) {targetInterceptor = (isStatic ?new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));}else {targetInterceptor = (isStatic ?new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));}// Choose a "direct to target" dispatcher (used for// unadvised calls to static targets that cannot return this).Callback targetDispatcher = (isStatic ?new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());Callback[] mainCallbacks = new Callback[] {aopInterceptor,  // targetInterceptor,  // invoke target without considering advice, if optimizednew SerializableNoOp(),  // no override for methods mapped to thistargetDispatcher, this.advisedDispatcher,new EqualsInterceptor(this.advised),new HashCodeInterceptor(this.advised)};Callback[] callbacks;// If the target is a static one and the advice chain is frozen,// then we can make some optimizations by sending the AOP calls// direct to the target using the fixed chain for that method.if (isStatic && isFrozen) {Method[] methods = rootClass.getMethods();Callback[] fixedCallbacks = new Callback[methods.length];this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length);// TODO: small memory optimization here (can skip creation for methods with no advice)for (int x = 0; x < methods.length; x++) {Method method = methods[x];List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());this.fixedInterceptorMap.put(method, x);}// Now copy both the callbacks from mainCallbacks// and fixedCallbacks into the callbacks array.callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);this.fixedInterceptorOffset = mainCallbacks.length;}else {callbacks = mainCallbacks;}return callbacks;
}

可以结合调试,方便理解

示例源码

https://github.com/realpdai/tech-pdai-spring-demos


http://chatgpt.dhexx.cn/article/0HPHf9fp.shtml

相关文章

Spring——AOP用到的代理模式SpringAOP实现原理

AOP&#xff0c;面向切面编程&#xff0c;是Spring框架中的核心思想之一&#xff1b;在Spring中是通过动态代理来实现的&#xff0c;在目标类的基础上增加切面逻辑&#xff0c;对原对象进行增强&#xff1b; SpringAOP的源码中用到了两种动态代理来实现拦截切入功能&#xff1…

深入分析 Spring 基于注解的 AOP 实现原理

一、AOP 的基本使用 AOP 的使用分为三步走&#xff1a; 将业务逻辑组件和切面类都加入到容器中&#xff1a;告诉 Spring 哪个是切面类&#xff1b;Aspect在切入类上的每一个通知方法上标注通知注解&#xff1a;告诉 Spring 何时何地运行&#xff08;切入点表达式&#xff09;…

灵魂画手图解Spring AOP实现原理!

本篇旨在让读者对Spring AOP实现原理有一个宏观上的认识&#xff0c;因此会丢失一些细节&#xff0c;具体实现参考&#xff1a;老实人Spring源码目录 阅读本篇文章前&#xff0c;希望读者对Spring Ioc以及Spring AOP的使用(Aspect)由一定了解&#xff0c;话不多说&#xff0c;直…

Spring AOP实现原理

1、Spring AOP Spring AOP的面向切面编程&#xff0c;是面向对象编程的一种补充&#xff0c;用于处理系统中分布的各个模块的横切关注点&#xff0c;比如说事务管理、日志、缓存等。它是使用动态代理实现的&#xff0c;在内存中临时为方法生成一个AOP对象&#xff0c;这个对象…

AOP实现原理详解

&#xfeff;&#xfeff; 转载地址&#xff1a;https://my.oschina.net/elain/blog/382494 一、什么是 AOP AOP&#xff08;Aspect-OrientedProgramming&#xff0c;面向切面编程&#xff09;&#xff0c;可以说是OOP&#xff08;Object-Oriented Programing&#xff0c;面向…

Spring框架的AOP实现原理

一、AOP的基本概念 AOP先是一种思想&#xff0c;后是一种技术。 AOP&#xff1a;面向切面编程&#xff0c;是将那些与业务无关&#xff08;比如有事务处理&#xff0c;日志管理&#xff0c;权限控制等&#xff09;&#xff0c;但要为业务模块共同调用的逻辑封装成一个可重用的…

Java:由浅入深揭开 AOP 实现原理

概述&#xff1a; 最近在开发中遇到了一个刚好可以用AOP实现的例子&#xff0c;就顺便研究了AOP的实现原理&#xff0c;把学习到的东西进行一个总结。文章中用到的编程语言为kotlin&#xff0c;需要的可以在IDEA中直接转为java。 这篇文章将会按照如下目录展开&#xff1a; A…

Spring的AOP原理

为什么80%的码农都做不了架构师&#xff1f;>>> 一、什么是 AOP AOP&#xff08;Aspect-OrientedProgramming&#xff0c;面向切面编程&#xff09;&#xff0c;可以说是OOP&#xff08;Object-Oriented Programing&#xff0c;面向对象编程&#xff09;的补充和完…

利用C语言实现动态数组

数组存在的问题&#xff1a;如果我们定义一个数组去存储数据&#xff0c;需要提前定义数组的个数&#xff0c;或者数组根据第一次存储的元素个数自动确定数组的大小&#xff0c;但是我们如果想对数组进行元素插入只能重新定义一个新数组&#xff0c;或者预定义一个空间非常大的…

C++数组之动态数组

目录 1.摘要 2.动态数组内存分配 1一维数组 2多维数组&#xff08;以2维为例&#xff09; 3.动态数组初始化 1默认初始化 2.自定义初始化 4.动态数组释放 5.例子 Gradebook类的实现 6.参考文章 1.摘要 数组是一种顺序存储的数据结构&#xff0c;在定义数组时&…

C++中如何定义动态数组

首先&#xff1a;为什么需要动态定义数组呢&#xff1f; 这是因为&#xff0c;很多情况下&#xff0c;在预编译过程阶段&#xff0c;数组的长度是不能预先知道的&#xff0c;必须在程序运行时动态的给出 但是问题是&#xff0c;c要求定义数组时&#xff0c;必须明确给定数组…

c++ 动态数组

动态数组 相关数组知识连接 数组详解 多维数组 在之前的文章中&#xff0c;讲解了数组的相关知识&#xff0c;那一种数组&#xff08;数组相关连接&#xff1a;https://blog.csdn.net/m0_62870588/article/details/123787052&#xff09;又称是静态数组&#xff0c;因为它的大小…

C语言中动态分配数组

很多人在编写C语言代码的时候很少使用动态数组&#xff0c;不管什么情况下通通使用静态数组的方法来解决&#xff0c;在当初学习C语言的时候我就是一个典型的例子&#xff0c;但是现在发现这是一个相当不好的习惯&#xff0c;甚至可能导致编写的程序出现一些致命的错误。尤其对…

C语言学习笔记:动态数组

动态数组 数组是C语言中的很重要的一种构造类型&#xff0c;最初我们学习的都是静态数组&#xff0c;但是&#xff0c;静态数组有着自己难以改变的缺点——数组长度固定。 一般在静态数组定义后&#xff0c;系统就会为其分配对应长度的连续的专有内存空间&#xff0c;可是&am…

C语言如何实现动态数组?

提出问题 请问在c语言里如何实现动态大小的数组啊&#xff0c;比如说int a[N];&#xff0c;这里N的值可以在程序中定&#xff0c;或者有什么方法可以实现类似的功能&#xff1f;总之只要在编译时不用制定数组大小就行。 分析问题 嵌入式系统的内存是宝贵的&#xff0c;内存是否…

C的动态数组的详细知识(网上收集到的大量详细知识以及个人理解的汇总)

动态数组是指在声明时没有确定数组大小的数组&#xff0c;即忽略圆括号中的下标&#xff1b;当要用它时&#xff0c;可随时用ReDim语句重新指出数组的大小。使用动态数组的优点是可以根据用户需要&#xff0c;有效利用存储空间。 可以了解动态数组的详细定义 一.C版本动态数组…

动态数组C语言实现详解

目录 0、前言 一、动态数组数据结构 二、动态数组增删改查函数声明 三、数组创建 1、头部动态创建 2、头部静态创建 四、元素添加 五、元素删除 1、根据元素值删除 2、根据元素位置删除 六、元素修改 七、元素查找 八、数组清空 九、数组销毁 十、验证程序 0、前…

C语言实现 动态数组 处理任意类型数据

引言&#xff1a;动态数组在C/C、Java、Python等语言中应用广泛&#xff0c;高级语言一般通过调用类或接口等可以快捷使用&#xff0c;C语言实现动态数组需要手动构造&#xff0c;以下为实现过程。 1 结构体构造动态数组 typedef struct Array {void **p; //维护在堆区…

C语言创建动态数组

C语言创建动态数组 1.编写步骤 1. 添加所需头文件 stdlib.h 该头文件下包含的与分配存储区相关的函数如下&#xff1a; void* malloc (size_t size);//从堆中分配size字节的存储空间 void* calloc (size_t num, size_t size);//分配数组并将数组零初始化。为 num 个元素的数…

在OpenCV里实现开运算

前面学习腐蚀和膨胀算法,并且深刻地认识到它们的特性以及作用。如果由这两种组合出来的运算又有什么样的不同呢?比如一个图像先腐蚀后膨胀的操作,会有什么结果呢?因为腐蚀是把图片白色变小,膨胀又是把图片白色变大,是否会保持原图不变呢?带着这些问题来研究一下先腐蚀后…