Spring AOP 默认使用的动态代理机制取决于目标对象是否实现了接口:
- 如果目标对象实现了接口,Spring AOP 默认使用 JDK 动态代理。
- 如果目标对象没有实现接口,Spring AOP 默认使用 CGLIB 动态代理。
这两种动态代理机制在实现方式和性能上有显著区别。以下是它们的详细对比:
1. JDK 动态代理
(1)实现原理
- JDK 动态代理基于 接口 实现。
- 通过
java.lang.reflect.Proxy
类动态生成代理类。 - 代理类实现了目标对象的接口,并将方法调用委托给
InvocationHandler
。
(2)使用条件
- 目标对象必须实现至少一个接口。
(3)示例代码
public interface UserService {
void save();
}
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("Save user");
}
}
public class JdkProxyDemo {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy1, method, args1) -> {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args1);
System.out.println("After method: " + method.getName());
return result;
}
);
proxy.save();
}
}
(4)优点
- 无需引入第三方库,直接使用 JDK 自带功能。
- 性能较好,适合代理接口方法。
(5)缺点
- 只能代理实现了接口的目标对象。
- 对于没有接口的类无法使用。
2. CGLIB 动态代理
(1)实现原理
- CGLIB(Code Generation Library)基于 继承 实现。
- 通过字节码技术动态生成目标类的子类,并重写父类的方法。
- 代理类继承了目标类,并拦截方法调用。
(2)使用条件
- 目标对象不需要实现接口。
- 目标类和方法不能是
final
的(因为需要继承和重写)。
(3)示例代码
public class UserService {
public void save() {
System.out.println("Save user");
}
}
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
System.out.println("Before method: " + method.getName());
Object result = proxy.invokeSuper(obj, args1);
System.out.println("After method: " + method.getName());
return result;
});
UserService proxy = (UserService) enhancer.create();
proxy.save();
}
}
(4)优点
- 可以代理没有接口的类。
- 功能更强大,支持对普通类的方法拦截。
(5)缺点
- 需要引入第三方库(CGLIB)。
- 性能略低于 JDK 动态代理,因为需要生成子类。
- 无法代理
final
类或final
方法。
3. JDK 动态代理 vs CGLIB 动态代理
特性 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
实现方式 | 基于接口,使用 Proxy 类生成代理。 | 基于继承,使用字节码技术生成子类。 |
使用条件 | 目标对象必须实现接口。 | 目标对象不需要实现接口。 |
性能 | 较高。 | 略低,因为需要生成子类。 |
依赖 | JDK 自带,无需额外依赖。 | 需要引入 CGLIB 库。 |
限制 | 无法代理没有接口的类。 | 无法代理 final 类或 final 方法。 |
适用场景 | 适合代理接口方法。 | 适合代理普通类的方法。 |
4. Spring AOP 的选择策略
Spring AOP 根据目标对象是否实现接口自动选择代理机制:
- 如果目标对象实现了接口,默认使用 JDK 动态代理。
- 如果目标对象没有实现接口,默认使用 CGLIB 动态代理。
如果需要强制使用 CGLIB 代理,可以通过配置 proxyTargetClass=true
实现:
(1)基于注解的配置
@EnableAspectJAutoProxy(proxyTargetClass = true)
(2)基于 XML 的配置
<aop:aspectj-autoproxy proxy-target-class="true"/>
5. 总结
- JDK 动态代理:基于接口实现,性能较高,但只能代理实现了接口的目标对象。
- CGLIB 动态代理:基于继承实现,功能更强大,可以代理没有接口的类,但性能略低。
Spring AOP 默认根据目标对象是否实现接口自动选择代理机制,同时也支持手动配置强制使用 CGLIB 代理。理解这两种代理机制的区别有助于在实际开发中更好地使用 Spring AOP。
THE END
暂无评论内容