jdk动态代理和cglib动态代理对比:
CGLIB 和 JDK 动态代理都可以用来在运行时生成代理对象
1. 基本概念
- JDK 动态代理 :只代理接口(interface),无法代理类。它使用
java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来实现动态代理。 - CGLIB 动态代理:可以代理类和接口。它通过生成目标类的子类来创建代理对象,因此可以代理没有实现接口的类。
2. 使用场景
- JDK 动态代理:适用于目标对象实现了一个或多个接口的情况。Spring AOP 默认使用 JDK 动态代理,如果目标对象没有实现接口,则使用 CGLIB。
- CGLIB 动态代理:适用于目标对象没有实现接口或者需要代理类而不是接口的情况。
3. 性能
- JDK 动态代理:由于使用了反射机制,性能相对较低,但对于简单接口的代理,性能差异不明显。
- CGLIB 动态代理:通过生成字节码来创建代理对象,性能比 JDK 动态代理更高,但是生成字节码的开销较大。
4. 优缺点总结
-
JDK 动态代理:
- 优点:简单,直接使用 JDK 提供的 API,无需额外依赖。
- 缺点:只能代理接口,性能较低。
-
CGLIB 动态代理:
- 优点:可以代理类和接口,性能较高。
- 缺点:需要额外的 CGLIB 库,生成字节码的开销较大,无法代理
final
类和方法。
jdk动态代理:
- 被代理的接口和实现类:
java
public interface Subject {
void doSomething();
}
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("Doing something in RealSubject");
}
}
- 创建
InvocationHandler
实现类:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method execution");
Object result = method.invoke(target, args);
System.out.println("After method execution");
return result;
}
}
- 使用
Proxy
创建代理对象:
java
import java.lang.reflect.Proxy;
public class JdkProxyDemo {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new MyInvocationHandler(realSubject)
);
proxy.doSomething();
}
}
cglib动态代理使用步骤
CGLIB(Code Generation Library)是一个强大的高性能代码生成库,它可以在运行时动态生成字节码。CGLIB 常用于创建代理对象,特别是在 Spring 框架中,用于 AOP(面向切面编程)
- 添加 CGLIB 依赖项:
如果你使用的是 Maven,可以在 pom.xml
中添加以下依赖项:
XML
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
- 被代理的类
java
public class RealSubject {
private String field;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public void doSomething() {
System.out.println("Doing something in RealSubject");
}
}
- 创建 MethodInterceptor:
java
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method execution");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method execution");
return result;
}
}
4.创建代理对象:
java
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new MyMethodInterceptor());
RealSubject proxy = (RealSubject) enhancer.create();
proxy.doSomething();
}
}
RealSubject
是被代理的类,MyMethodInterceptor
实现了 MethodInterceptor
接口,用于在方法执行前后添加额外的逻辑。Enhancer
类用于创建代理对象。
运行 CglibProxyDemo
类时,你会看到如下输出:
cglib jdk17 的问题:
jdk17 需要增加如下配置,否则会 报错:
--add-opens=java.base/java.lang=ALL-UNNAMED
在 JDK 9 及其之后的版本(包括 JDK 17)中,Java 引入了模块系统(Project Jigsaw),这使得访问某些内部 API 和模块变得更加严格。--add-opens
参数允许你在运行 Java 应用时开放特定的模块和包给未命名模块(通常是类路径上的代码),以便它们可以进行深层次的反射操作。
报错信息:
XML
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @4cdf35a9
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
at net.sf.cglib.core.ReflectUtils$1.run(ReflectUtils.java:61)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
at net.sf.cglib.core.ReflectUtils.<clinit>(ReflectUtils.java:52)
at net.sf.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:243)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:332)
... 13 more