动态代理
首先,什么是代理?
代理模式:
代理是框架诸多技术的底层依赖,代理是通过限制对象的直接访问,即不能通过 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, args | obj, method, args, methodProxy |
| 调用方式 | method.invoke(target, args) | proxy.invokeSuper(obj, args) |
SpringBoot 为什么使用 cglib?
Spring Boot默认使用CGLIB,原因有以下几点:
- 灵活性:Spring的AOP常用于无接口的类(比如普通的POJO),CGLIB无需接口支持更通用。
- 性能:CGLIB避免反射,执行效率更高,尤其在高并发场景下。
- 集成方便:Spring早就把CGLIB集成进来了(
spring-core依赖里包含),无需额外配置。
你说记不住API,其实没必要死记硬背。我们抓住几个关键点:
- JDK动态代理:想到“接口”和“反射”,API主要是
Proxy.newProxyInstance和InvocationHandler。 - CGLIB:想到“继承”和“效率高”,API主要是
Enhancer和MethodInterceptor,记住methodProxy是性能关键。 - 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 生成一个被代理对象的子类来作为代理,如下图所示: