JDK动态代理与CGLIB动态代理的区别是什么?

JDK 动态代理 是通过 接口 来实现的代理,只有目标类实现了接口,才能被代理。
而 CGLIB 动态代理 则是通过 继承目标类 来生成代理对象,它可以代理没有接口的类。
- JDK 代理 适合用来代理那些有接口的类。
- CGLIB 代理 适合用来代理那些没有接口的类,尤其是当目标类不能修改时。
知识内容
JDK 动态代理
JDK 动态代理是 Java 原生支持的代理机制,要求目标类必须实现接口。代理对象在运行时通过 Proxy 类创建,并需要一个 InvocationHandler 来处理方法调用。
关键特点
- 基于接口:JDK 动态代理只支持接口的代理,代理对象必须实现目标类的接口。
- 运行时代理:代理对象是运行时通过反射机制动态生成的,而不是编译时生成。
- 灵活性和可扩展性:JDK 动态代理非常适用于接口的代理,可以在运行时选择代理对象的行为。
JDK 动态代理原理
JDK 动态代理的核心是 InvocationHandler 接口,通过该接口可以拦截所有方法调用,并在目标方法执行前后执行增强逻辑。Proxy.newProxyInstance() 方法用来生成代理类,代理类的所有方法都会被 invoke() 方法拦截。
import java.lang.reflect.*;
interface HelloService {
void sayHello(String name);
}
class HelloServiceImpl implements HelloService {
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}
class HelloServiceProxy implements InvocationHandler {
private Object target;
public HelloServiceProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
public class ProxyExample {
public static void main(String[] args) {
HelloService helloService = new HelloServiceImpl();
HelloService proxy = (HelloService) Proxy.newProxyInstance(
helloService.getClass().getClassLoader(),
helloService.getClass().getInterfaces(),
new HelloServiceProxy(helloService));
proxy.sayHello("World");
}
}
分析:
- HelloService 是接口,HelloServiceImpl 是实现类,HelloServiceProxy 是代理类。
- HelloServiceProxy 实现了 InvocationHandler 接口,重写了 invoke() 方法,在目标方法执行前后进行增强。
JDK 动态代理的限制
- 只能代理接口:只能用于代理实现了接口的类。如果目标类没有实现接口,JDK 动态代理无法使用。
- 性能相对较差:由于代理对象通过反射机制生成,并且每次方法调用都会触发反射,性能会略微受到影响。
CGLIB 动态代理
CGLIB (Code Generation Library) 是一个第三方库,它通过 继承 目标类并重写其方法来生成动态代理。与 JDK 动态代理不同,CGLIB 代理不需要目标类实现接口,因此能够代理普通类。
关键特点
- 基于继承:CGLIB 动态代理通过生成目标类的子类来创建代理对象。目标类无需实现接口。
- 字节码生成:CGLIB 使用字节码技术生成代理类,能够高效地实现代理。
- 代理生成方式:CGLIB 会通过继承目标类并重写目标类的方法来实现增强。
CGLIB 动态代理原理
CGLIB 动态代理的原理是通过 字节码操作,生成目标类的代理子类,并重写目标类的方法来进行增强。这意味着生成的代理类是目标类的一个子类,并且对方法调用进行拦截。
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
class HelloService {
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}
class HelloServiceProxy implements MethodInterceptor {
private Object target;
public HelloServiceProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
public class CglibProxyExample {
public static void main(String[] args) {
HelloService helloService = new HelloService();
HelloServiceProxy helloServiceProxy = new HelloServiceProxy(helloService);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloService.class);
enhancer.setCallback(helloServiceProxy);
HelloService proxy = (HelloService) enhancer.create();
proxy.sayHello("World");
}
}
分析:
- HelloService 是目标类,HelloServiceProxy 是代理类,Enhancer 用来生成代理类。
- CGLIB 动态代理通过继承目标类生成代理子类,并重写目标类的方法进行增强。
CGLIB 的限制
- 不能代理 final 类和方法:由于 CGLIB 是基于继承的方式实现代理,因此它无法代理 final 类和 final 方法。
- 内存消耗较大:CGLIB 需要生成新的字节码类,因此在内存和性能上较为消耗,特别是在大量代理对象的情况下。
知识拓展
JDK 动态代理 vs CGLIB 动态代理
| 特性 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 代理方式 | 基于接口代理 | 基于类的继承代理 |
| 目标要求 | 目标类必须实现接口 | 目标类无需实现接口 |
| 代理类生成方式 | 使用反射生成代理类 | 通过字节码生成目标类的子类 |
| 性能 | 由于基于反射,性能较差 | 性能较好,因为直接生成子类 |
| 线程安全 | 和 CGLIB 一样,默认非线程安全 | 和 JDK 动态代理一样,默认非线程安全 |
| 限制 | 只能代理实现了接口的类 | 无法代理 final 类和方法 |
适用场景:
- JDK 动态代理:适用于目标类实现了接口,并且我们希望基于接口进行代理的场景。常见的应用场景是 Spring AOP,它通常使用 JDK 动态代理来增强服务层接口的方法。
- CGLIB 动态代理:适用于目标类没有实现接口,或者需要对类的内部方法进行代理的场景。例如,Spring 的事务管理(@Transactional)通常使用 CGLIB 代理,如果目标类没有实现接口。
Spring AOP 中的动态代理
在 Spring AOP 中,动态代理用于增强业务逻辑。当你使用注解(例如 @Transactional)时,Spring 会根据目标对象的类型选择适合的代理方式:
- JDK 动态代理:如果目标对象实现了接口,Spring 会使用 JDK 动态代理。
- CGLIB 动态代理:如果目标对象没有实现接口,Spring 会使用 CGLIB 动态代理生成子类。
结语
- JDK 动态代理适用于有接口的类,性能较差但灵活。
- CGLIB 动态代理适用于没有接口的类,性能较好,但无法代理 final 类和方法。
- 在实际项目中,根据目标对象的实现方式(是否有接口),可以选择合适的代理方式。特别是在 Spring 框架中,选择 JDK 动态代理或 CGLIB 动态代理取决于目标类是否实现了接口。
通过了解 JDK 动态代理和 CGLIB 动态代理的原理和区别,你能够在实际开发中更加灵活地选择和使用代理技术,提高代码的可维护性和扩展性。
以上关于JDK动态代理与CGLIB动态代理的区别是什么?的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » JDK动态代理与CGLIB动态代理的区别是什么?

微信
支付宝