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动态代理:
1. 被代理的接口和实现类:
public interface Subject {void doSomething();
}public class RealSubject implements Subject {@Overridepublic void doSomething() {System.out.println("Doing something in RealSubject");}
}
2. 创建 InvocationHandler
实现类:
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;}@Overridepublic 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;}
}
3. 使用 Proxy
创建代理对象:
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(面向切面编程)
1. 添加 CGLIB 依赖项:
如果你使用的是 Maven,可以在 pom.xml
中添加以下依赖项:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
2. 被代理的类
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");}
}
3. 创建 MethodInterceptor:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic 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.创建代理对象:
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 应用时开放特定的模块和包给未命名模块(通常是类路径上的代码),以便它们可以进行深层次的反射操作。
报错信息:
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 @4cdf35a9at 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