Spring 代理对象详解

Spring 代理对象详解
penjc1. 什么是代理对象?
代理对象是指一个对象,它代表了另一个对象,并对外提供相同的接口。代理对象可以控制对目标对象(也叫做原始对象、被代理对象)的访问,通常用于:
- 在方法调用前后插入某些额外操作(如日志、权限验证、事务管理等)
- 延迟对象的创建(如懒加载)
- 控制访问(如缓存)
在 Spring 中,代理对象主要通过 动态代理 实现,通常是通过 JDK 动态代理或者 CGLIB 代理技术。
2. Spring 中的代理类型
Spring 通过两种方式来实现代理:
- JDK 动态代理:基于接口的代理,只能代理接口实现类。它会在运行时通过反射机制动态生成一个实现了接口的代理类,并把对接口方法的调用转发到目标对象。
- CGLIB 代理:基于子类的代理,可以代理没有实现接口的类。它通过生成目标类的子类,并重写目标类的方法来实现代理。
2.1 JDK 动态代理
JDK 动态代理的核心是 java.lang.reflect.Proxy
类,它创建一个接口的实现类,且所有的方法调用都会被代理对象捕获,并转发给 InvocationHandler
进行处理。Spring 使用 JDK 动态代理时,目标对象必须实现接口。
示例:JDK 动态代理
1 | public interface MathService { |
1 | public class LoggingHandler implements InvocationHandler { |
1 | public class Main { |
输出:
1 | 调用方法: add |
在这个例子中,MathServiceImpl
是目标对象,LoggingHandler
是 InvocationHandler,Proxy.newProxyInstance
用于生成代理对象。
2.2 CGLIB 代理
CGLIB(Code Generation Library)是一个开源的字节码增强库,它可以在运行时动态生成一个目标类的子类,并重写目标类的方法,从而实现代理。
CGLIB 代理不需要目标类实现接口,因此它可以用于没有接口的类。CGLIB 代理的实现是通过继承的方式创建代理类,并重写父类的方法来插入增强逻辑。
Spring 会使用 CGLIB 代理的方式来生成目标类的子类,通常这种代理方式适用于没有实现接口的类。
示例:CGLIB 代理
1 | public class MathService { |
1 | public class LoggingInterceptor implements MethodInterceptor { |
1 | public class Main { |
在这个例子中,MathService
是目标对象,LoggingInterceptor
是增强逻辑,ProxyFactory
用于创建代理对象。
输出:
1 | 调用方法: add |
3. Spring AOP 中的代理对象
在 Spring AOP 中,代理对象的使用是通过 切面(Aspect)来实现的。Spring AOP 默认使用 JDK 动态代理,如果目标对象实现了接口,它将使用 JDK 动态代理;如果目标对象没有实现接口,则会使用 CGLIB 代理。
3.1 JDK 动态代理 在 Spring AOP 中的应用
当你使用 @Aspect
注解来定义一个切面时,Spring 会自动创建一个代理对象,将切面逻辑应用到目标对象的相关方法上。如果目标对象实现了接口,Spring 会使用 JDK 动态代理。
示例:Spring AOP 与 JDK 动态代理
1 |
|
1 | public interface MathService { |
1 | <!-- Spring 配置 --> |
在这个例子中,Spring 会创建 MathServiceImpl
的代理对象,当调用 add
方法时,会先执行 LoggingAspect
中的 logBefore()
方法。
3.2 CGLIB 代理 在 Spring AOP 中的应用
如果目标类没有实现接口,Spring 会使用 CGLIB 代理。CGLIB 代理会生成目标类的子类,并重写目标方法。使用 CGLIB 代理时,切面逻辑仍然会应用于目标方法。
示例:Spring AOP 与 CGLIB 代理
1 |
|
1 |
|
1 | <!-- Spring 配置 --> |
在这个例子中,proxy-target-class="true"
表示强制使用 CGLIB 代理,即使目标类没有实现接口,Spring 也会使用 CGLIB 生成一个子类。
4. 总结
- 代理对象:Spring 通过代理模式,使用 JDK 动态代理或 CGLIB 代理实现 AOP,允许你在目标方法前后执行额外的操作(如日志、事务等)。
- JDK 动态代理:只能代理接口类型的目标对象,使用
java.lang.reflect.Proxy
类来创建代理。 - CGLIB 代理:可以代理没有实现接口的类,使用 CGLIB 库通过继承目标类来实现代理。
- Spring AOP:默认使用 JDK 动态代理,当目标对象实现接口时,使用 JDK 动态代理;否则,使用 CGLIB 代理。
代理对象是 Spring 实现 AOP 和其他功能(如事务管理、缓存、延迟加载等)的基础,使得代码更加松耦合,便于扩展和维护。