【spring学习笔记01】动态代理

114 阅读5分钟

动态代理

首先,什么是代理?

代理模式:

代理是框架诸多技术的底层依赖,代理是通过限制对象的直接访问,即不能通过 new 的方式得到想要的对象,而是通过访问该对象的代理类来创建对象。这样,我们就保护了内部对象。

如果有一天内部对象因为某个原因,换个了名或者换了个方法字段,那对访问者来说一点也不影响,因为他拿到的只是代理类,从而使该访问对象具有高扩展性。

按照代理的创建时期,代理可分为静态代理和动态代理:静态代理是开发者手动创建,程序运行前已经存在。动态代理不需要手动创建,在程序运行时动态创建 代理类。

代理模式-- 给某个对象提供一个代理,以改变该对象的访问方式

动态代理:

jdk 代理和 cglib 代理

jdk 代理的核心是java.lang.reflect.Proxy 类,它通过运行时生成一个目标接口的代理类,来完成 代理功能。

1、首先有个接口

2、目标类实现这个接口

3、用 Proxy.newProxyInstance 生成代理对象,指定接口和处理器

4、调用代理对象的方法,交给 invoke 方法执行

cglib 代理

cglib 是第三方库,通过字节码操作在运行时生成目标类的子类作为代理类,代理类会重写父类方法,并插入额外的逻辑。

cglib 代理是 spring 已经集成的方式。

维度JDK动态代理CGLIB动态代理
实现方式基于接口(通过Proxy类)基于继承(生成子类)
性能用反射,稍慢字节码操作,效率更高
依赖JDK自带,无需额外库需要CGLIB库(Spring已集成)
适用场景必须有接口无接口也能用,但不能是final类
回调参数proxy, method, argsobj, method, args, methodProxy
调用方式method.invoke(target, args)proxy.invokeSuper(obj, args)

SpringBoot 为什么使用 cglib?

Spring Boot默认使用CGLIB,原因有以下几点:

  1. 灵活性:Spring的AOP常用于无接口的类(比如普通的POJO),CGLIB无需接口支持更通用。
  2. 性能:CGLIB避免反射,执行效率更高,尤其在高并发场景下。
  3. 集成方便:Spring早就把CGLIB集成进来了(spring-core依赖里包含),无需额外配置。

你说记不住API,其实没必要死记硬背。我们抓住几个关键点:

  1. JDK动态代理:想到“接口”和“反射”,API主要是Proxy.newProxyInstanceInvocationHandler
  2. CGLIB:想到“继承”和“效率高”,API主要是EnhancerMethodInterceptor,记住methodProxy是性能关键。
  3. Spring Boot:默认CGLIB,记“灵活+性能”就够了。

  • 有没有接口?有→JDK动态代理,没→CGLIB。
  • 性能敏感吗?敏感→CGLIB。
  • Spring默认啥?CGLIB!

aop 底层:反射+代理

aop 之所以叫面向切面编程,是因为它的核心思想是将横切关注点从核心业务逻辑中分离出来,形成一个个切面。

aop 的术语

  • 横切关注点(cross-cutting concerns) :多个类或对象中的公共行为(如日志记录、事务管理、权限控制、接口限流、接口幂等等)。
  • 切面(Aspect) :对横切关注点进行封装的类,一个切面是一个类。切面可以定义多个通知,用来实现具体的功能。
  • 连接点(JoinPoint) :连接点是方法调用或者方法执行时的某个特定时刻(如方法调用、异常抛出等)。
  • 通知(Advice) :通知就是切面在某个连接点要执行的操作。通知有五种类型,分别是前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)和环绕通知(Around)。前四种通知都是在目标方法的前后执行,而环绕通知可以控制目标方法的执行过程。
  • 切点(Pointcut) :一个切点是一个表达式,它用来匹配哪些连接点需要被切面所增强。切点可以通过注解、正则表达式、逻辑运算等方式来定义。比如 execution(* com.xyz.service..*(..))匹配 com.xyz.service 包及其子包下的类或接口。
  • 织入(Weaving) :织入是将切面和目标对象连接起来的过程,也就是将通知应用到切点匹配的连接点上。常见的织入时机有两种,分别是编译期织入(Compile-Time Weaving 如:AspectJ)和运行期织入(Runtime Weaving 如:AspectJ、Spring AOP)。

通知类型

  • Before(前置通知):目标对象的方法调用之前触发
  • After (后置通知):目标对象的方法调用之后触发
  • AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发
  • AfterThrowing(异常通知):目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
  • Around (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法

aop 和代理的关系

AOP 的常见实现方式有动态代理、字节码操作等方式。

Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象;而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示: