29. Spring AOP中的动态代理和CGLIB代理有什么区别?
大约 3 分钟
pring AOP 使用两种主要的代理机制来实现切面功能:JDK 动态代理和CGLIB 代理。它们各自有不同的实现方式和适用场景。以下是这两者的主要区别:
1. JDK 动态代理
实现方式:
- JDK 动态代理基于 Java 的接口机制。它使用
java.lang.reflect.Proxy
类和InvocationHandler
接口来动态创建代理对象。 - 代理类是实现了指定接口的动态生成的类。代理对象通过实现的接口来进行方法调用,而这些方法调用被
InvocationHandler
拦截并处理。
适用场景:
- 适用于目标类实现了一个或多个接口的情况。Spring AOP 在默认情况下会优先使用 JDK 动态代理,如果目标类实现了接口,则使用这种方式。
优点:
- 代理类非常轻量,性能通常较好。
- 无需依赖第三方库,完全使用 JDK 提供的功能。
缺点:
- 只能代理接口方法,不能代理类中没有实现接口的方法。
- 如果目标类没有实现接口,则无法使用 JDK 动态代理。
示例:
public interface MyService {
void performTask();
}
public class MyServiceImpl implements MyService {
public void performTask() {
System.out.println("Performing task in MyService");
}
}
2. CGLIB 代理
实现方式:
- CGLIB(Code Generation Library)是一个强大的字节码生成库,它通过在运行时生成目标类的子类来创建代理对象。
- 代理对象是通过继承目标类生成的,因此可以代理没有实现接口的类。
适用场景:
- 适用于目标类没有实现接口的情况。Spring AOP 在目标类没有实现任何接口时,会使用 CGLIB 代理。
优点:
- 能够代理目标类的所有方法,包括没有在接口中定义的方法。
- 可以代理没有实现接口的类,这使得它在某些情况下比 JDK 动态代理更灵活。
缺点:
- 由于代理类是通过继承生成的,目标类和方法不能是
final
,否则无法生成代理。 - 相对 JDK 动态代理,CGLIB 的代理类生成稍微复杂一些,可能会稍微影响性能。
- 需要额外的 CGLIB 库。
示例:
public class MyService {
public void performTask() {
System.out.println("Performing task in MyService");
}
}
3. Spring AOP 的代理选择策略
- 默认策略:Spring AOP 默认会使用 JDK 动态代理,如果目标类实现了至少一个接口。
- 强制使用 CGLIB:可以通过在配置中设置
@EnableAspectJAutoProxy(proxyTargetClass = true)
或者在 XML 配置中使用<aop:config proxy-target-class="true"/>
来强制使用 CGLIB 代理,即使目标类实现了接口。
4. 总结
- JDK 动态代理:适用于目标类实现了接口的情况,通过接口生成代理类,轻量且高效,但只能代理接口方法。
- CGLIB 代理:适用于目标类没有实现接口的情况,通过生成目标类的子类来实现代理,灵活性更高,但需要目标类和方法非
final
且稍微影响性能。
在实际开发中,如果可能,通常推荐通过接口定义服务,因为这不仅符合面向接口编程的原则,还能更好地利用 JDK 动态代理的优势。而在需要代理没有实现接口的类时,可以使用 CGLIB 代理。