JDK动态代理和CGLIB动态代理是两种常用的Java代理技术,它们在实现原理、使用场景和性能等方面存在一些区别。下面我们详细比较一下这两种代理方式:
1. 实现原理
JDK动态代理
- 接口:JDK动态代理只能针对接口进行代理,即代理类必须实现一个或多个接口。
- 反射机制:JDK动态代理通过
java.lang.reflect.Proxy类和InvocationHandler接口实现,利用反射机制在运行时生成代理类。
public interface MyService {
void performAction();
}
public class MyServiceImpl implements MyService {
@Override
public void performAction() {
System.out.println("Performing action...");
}
}
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 invocation");
Object result = method.invoke(target, args);
System.out.println("After method invocation");
return result;
}
}
// 使用JDK动态代理
MyService originalService = new MyServiceImpl();
MyService proxyService = (MyService) Proxy.newProxyInstance(
originalService.getClass().getClassLoader(),
new Class[]{MyService.class},
new MyInvocationHandler(originalService)
);
proxyService.performAction();
CGLIB动态代理
- 子类继承:CGLIB动态代理通过生成目标类的子类并覆盖其方法来实现代理,因此不需要目标类实现接口。
- 字节码生成:CGLIB(Code Generation Library)使用ASM库直接操作字节码在运行时生成代理类。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method invocation");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method invocation");
return result;
}
}
// 使用CGLIB动态代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyServiceImpl.class);
enhancer.setCallback(new MyMethodInterceptor());
MyServiceImpl proxyService = (MyServiceImpl) enhancer.create();
proxyService.performAction();
2. 使用场景
-
JDK动态代理:
- 适用于有接口定义的情况。
- 不要求目标类继承具体类,只需实现接口即可。
- 简单易用,但仅限于接口方法的代理。
-
CGLIB动态代理:
- 适用于没有接口定义的情况或需要代理类的方法较多的情况。
- 可以代理具体类及其方法。
- 较为复杂,需要处理目标类可能没有无参构造方法的问题。
3. 性能对比
-
JDK动态代理:
- 基于Java反射机制,性能相对较低,因为每次调用都涉及反射操作。
- 在接口方法数量少时性能影响较小。
-
CGLIB动态代理:
- 基于字节码生成,性能较高,因为生成的子类直接调用父类方法,无需反射。
- 在方法调用频繁或方法数量多时性能优势明显。
4. 代码复杂度
-
JDK动态代理:
- 代码简单,直接通过标准库中的
Proxy类和InvocationHandler接口实现。 - 不需第三方库支持。
- 代码简单,直接通过标准库中的
-
CGLIB动态代理:
- 代码较复杂,需要引入CGLIB库,并且涉及字节码操作。
- 对新手来说学习成本较高,但灵活性更强。
5. 限制和局限性
-
JDK动态代理:
- 只能代理接口,无法代理没有实现接口的类。
-
CGLIB动态代理:
- 不能代理标记为
final的类和方法,因为CGLIB需要生成子类。 - 需要目标类有默认的无参构造方法。
- 不能代理标记为
6. 框架支持
-
Spring AOP:
- 默认情况下,Spring AOP会优先使用JDK动态代理,如果目标类没有实现任何接口,则使用CGLIB动态代理。
<!-- Spring 配置 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
在上述配置中,proxy-target-class="true"表示强制使用CGLIB代理。
Spring源码使用jdk动态代理和cglib动态代理例子
Spring框架在内部实现中使用了JDK动态代理和CGLIB动态代理来增强Bean的功能,主要用于AOP(面向切面编程)和其他功能。以下是Spring源码中两个例子的解释:一个是使用JDK动态代理,另一个是使用CGLIB动态代理。
1. Spring源码中使用JDK动态代理的例子
Spring AOP默认情况下会优先使用JDK动态代理,如果目标类实现了接口。那么,Spring会通过Proxy.newProxyInstance方法来创建代理对象。
示例代码:
假设我们有一个简单的服务接口和其实现类:
public interface MyService {
void perform();
}
public class MyServiceImpl implements MyService {
@Override
public void perform() {
System.out.println("Performing operation");
}
}
然后,我们可以定义一个切面来拦截这个服务的方法调用:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.MyService.perform(..))")
public void logBefore() {
System.out.println("Log before method execution");
}
}
在Spring配置文件或Java配置类中启用AOP:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy // 默认使用JDK动态代理
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
@Bean
public LoggingAspect loggingAspect() {
return new LoggingAspect();
}
}
当我们获取MyService Bean并调用其perform方法时,Spring会使用JDK动态代理来创建代理对象,从而实现AOP功能。
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = context.getBean(MyService.class);
myService.perform();
}
}
输出将会是:
Log before method execution
Performing operation
2. Spring源码中使用CGLIB动态代理的例子
如果目标类没有实现任何接口,或者我们强制要求使用CGLIB代理,Spring会使用CGLIB来创建代理对象。这可以通过设置proxy-target-class属性来实现。
示例代码:
假设我们有一个没有实现接口的服务类:
public class AnotherService {
public void perform() {
System.out.println("Performing operation in AnotherService");
}
}
同样,我们可以定义一个切面来拦截这个服务的方法调用:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.AnotherService.perform(..))")
public void logBefore() {
System.out.println("Log before method execution in AnotherService");
}
}
在Spring配置文件或Java配置类中启用AOP,并强制使用CGLIB代理:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB动态代理
public class AppConfig {
@Bean
public AnotherService anotherService() {
return new AnotherService();
}
@Bean
public LoggingAspect loggingAspect() {
return new LoggingAspect();
}
}
当我们获取AnotherService Bean并调用其perform方法时,Spring会使用CGLIB动态代理来创建代理对象,从而实现AOP功能。
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
AnotherService anotherService = context.getBean(AnotherService.class);
anotherService.perform();
}
}
输出将会是:
Log before method execution in AnotherService
Performing operation in AnotherService
总结
- JDK动态代理适用于目标对象已经实现接口的情况。它通过生成一个实现相同接口的新类来创建代理对象。
- CGLIB动态代理适用于目标对象没有实现接口的情况,或者需要强制使用类代理的情况