Spring AOP 支持的两种模式动态代理(JDK Proxy 和 cglib)
·
在 Spring 中,AOP(面向切面编程)可以通过动态代理实现。Spring AOP 支持两种主要的动态代理模式:JDK Proxy 和 CGLIB Proxy。这两种方式各有特点,适用于不同的场景。
1. JDK 动态代理
JDK Proxy 是基于 Java 原生的反射机制实现的,使用 java.lang.reflect.Proxy 类生成代理对象。JDK Proxy 只能对实现了接口的类进行代理,因此只适用于代理那些有接口的目标类。
特点:
- 基于接口:JDK Proxy 只能代理实现了接口的类。如果目标类没有实现接口,无法使用 JDK Proxy。
- 性能较好:由于 JDK Proxy 是基于 Java 原生的反射机制,生成的代理对象性能通常较为优秀。
- 代理方式:JDK Proxy 通过接口实现代理,所以目标类和代理类之间通过接口进行调用。
使用示例:
假设有一个接口 UserService 和其实现类 UserServiceImpl:
public interface UserService {
void createUser();
void deleteUser();
}
public class UserServiceImpl implements UserService {
@Override
public void createUser() {
System.out.println("Creating user");
}
@Override
public void deleteUser() {
System.out.println("Deleting user");
}
}
配置 AOP:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
@Bean
public UserService userService() {
return new UserServiceImpl();
}
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
}
定义切面(Aspect):
@Aspect
public class MyAspect {
@Before("execution(* com.example.UserService.createUser(..))")
public void beforeCreateUser() {
System.out.println("Before createUser method");
}
@After("execution(* com.example.UserService.deleteUser(..))")
public void afterDeleteUser() {
System.out.println("After deleteUser method");
}
}
在这个例子中,UserServiceImpl 必须实现 UserService 接口,Spring 使用 JDK Proxy 为 UserService 接口创建代理对象。
2. CGLIB 动态代理
CGLIB Proxy 是基于字节码生成的动态代理工具,能够创建目标类的子类。与 JDK Proxy 不同,CGLIB Proxy 不要求目标类必须实现接口,可以代理没有实现接口的普通类。
特点:
- 基于继承:CGLIB Proxy 是通过继承目标类创建子类,并在子类中织入增强逻辑。
- 适用于无接口类:CGLIB Proxy 可以代理没有实现接口的类,而 JDK Proxy 不能。
- 性能稍差:由于使用字节码生成和继承机制,CGLIB Proxy 的性能通常稍逊色于 JDK Proxy。
使用示例:
假设我们有一个没有接口的目标类 OrderService:
public class OrderService {
public void createOrder() {
System.out.println("Creating order");
}
public void cancelOrder() {
System.out.println("Canceling order");
}
}
配置 AOP:
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用 CGLIB 代理
public class AopConfig {
@Bean
public OrderService orderService() {
return new OrderService();
}
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
}
定义切面(Aspect):
@Aspect
public class MyAspect {
@Before("execution(* com.example.OrderService.createOrder(..))")
public void beforeCreateOrder() {
System.out.println("Before createOrder method");
}
@After("execution(* com.example.OrderService.cancelOrder(..))")
public void afterCancelOrder() {
System.out.println("After cancelOrder method");
}
}
在这个例子中,OrderService 类没有实现接口,Spring 会自动使用 CGLIB 代理类来为 OrderService 类创建代理对象。
3. JDK Proxy 与 CGLIB Proxy 区别
| 特性 | JDK Proxy | CGLIB Proxy |
|---|---|---|
| 代理对象的类型 | 代理接口 | 代理类的子类 |
| 要求目标对象 | 必须实现接口 | 不需要接口,目标类可以没有接口 |
| 代理方式 | 动态代理接口 | 动态生成子类,重写方法 |
| 性能 | 更优 | 相对稍慢 |
| 适用场景 | 适用于代理接口 | 适用于没有实现接口的类 |
| Spring 配置 | 默认(只要有接口就使用 JDK Proxy) | @EnableAspectJAutoProxy(proxyTargetClass = true) 或 proxyTargetClass = true 配置 |
4. 选择使用 JDK Proxy 还是 CGLIB Proxy
- 如果目标类实现了接口,JDK Proxy 是首选,因为它性能更好,且不需要生成子类。
- 如果目标类没有实现接口,并且需要进行代理,CGLIB Proxy 是唯一选择。
- 如果希望强制使用 CGLIB,即使目标类有接口,可以在
@EnableAspectJAutoProxy注解中设置proxyTargetClass = true,或者在 XML 配置中设置<aop:proxy proxy-target-class="true"/>。
5. 总结
- JDK Proxy:通过接口实现代理,适用于目标类实现接口的情况。默认情况下,Spring 会使用 JDK Proxy。
- CGLIB Proxy:通过继承目标类实现代理,适用于没有实现接口的目标类。CGLIB Proxy 性能稍逊,但在没有接口的情况下是必需的。
在 Spring 中,代理的选择是由是否实现接口以及 @EnableAspectJAutoProxy 中的配置决定的。默认情况下,Spring 会选择 JDK Proxy,如果需要使用 CGLIB Proxy,则可以通过配置来显式选择。
更多推荐


所有评论(0)