JDK动态代理和CGLib动态代理
JDK动态代理和CGLib动态代理是Java中实现AOP(面向切面编程)的两种核心技术,它们在实现方式、依赖关系和使用场景上有显著差异。以下是对两者的详细对比:
JDK动态代理
用法
依赖接口:被代理的类必须实现至少一个接口。
核心类:
java.lang.reflect.Proxy
:生成代理对象。java.lang.reflect.InvocationHandler
:定义代理逻辑。
实现步骤:
// 1. 定义接口 public interface UserService { void addUser(String name); } // 2. 实现接口 public class UserServiceImpl implements UserService { public void addUser(String name) { System.out.println("添加用户: " + name); } } // 3. 实现InvocationHandler public class LogHandler implements InvocationHandler { private final Object target; public LogHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用前: " + method.getName()); Object result = method.invoke(target, args); System.out.println("调用后"); return result; } } // 4. 生成代理对象 UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[]{UserService.class}, new LogHandler(new UserServiceImpl()) ); proxy.addUser("Alice");
特点
基于接口:只能代理接口中定义的方法。
无需依赖:JDK自带,无需第三方库。
反射调用:通过反射执行目标方法,性能略低。
CGLib动态代理
用法
继承方式:通过生成被代理类的子类覆盖方法。
核心类:
Enhancer
:生成代理对象。MethodInterceptor
:定义代理逻辑。
实现步骤:
// 1. 目标类(无需接口) public class UserService { public void addUser(String name) { System.out.println("添加用户: " + name); } } // 2. 实现MethodInterceptor public class LogInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("调用前: " + method.getName()); Object result = proxy.invokeSuper(obj, args); // 调用父类方法 System.out.println("调用后"); return result; } } // 3. 生成代理对象 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new LogInterceptor()); UserService proxy = (UserService) enhancer.create(); proxy.addUser("Bob");
特点
基于继承:可代理未实现接口的类。
依赖第三方库:需引入
cglib
或Spring的spring-core
。字节码操作:通过ASM直接生成字节码,性能更高。
限制:无法代理
final
类或方法。
核心区别
使用场景
JDK动态代理:适用于接口明确的场景(如远程方法调用、服务层接口)。
CGLib动态代理:适用于无接口的类或需要高性能的场景(如Spring默认使用CGLib代理未实现接口的类)。
Spring中的选择
默认行为:若目标类实现接口,Spring使用JDK代理;否则使用CGLib。
强制CGLib:可通过
@EnableAspectJAutoProxy(proxyTargetClass = true)
强制使用CGLib。
总结
JDK动态代理更轻量但依赖接口,适合简单场景。
CGLib动态代理更灵活且性能更高,适合复杂场景但需注意依赖和限制。根据项目需求选择合适方案。
开启新对话