JDK动态代理和CGLIB动态代理的区别及适用场景解析

  • 发布时间:2023-09-21 17:09:15
  • 本文热度:浏览 868 赞 1 评论 0
  • 全文共1字,阅读约需1分钟

1. JDK动态代理和CGLIB动态代理的区别

1.1 前言

在软件开发中,代理模式是一种常见的设计模式,它提供了额外的中间层,以实现对目标对象的间接访问。通过使用代理,我们可以在不修改目标对象的情况下,对其进行功能扩展或增强。在实际项目中,我们经常会遇到需要使用动态代理的场景。而在Java中,最常用的两种动态代理实现方式分别是JDK动态代理和CGLIB动态代理。本文将详细介绍这两种动态代理方式的区别。

1.2 JDK动态代理

JDK动态代理是Java提供的一种基于接口的代理实现方式。它通过在运行时动态生成代理类,并实现目标接口,从而实现对目标对象的代理访问。JDK动态代理的核心类是java.lang.reflect.Proxy

1.2.1 使用案例

下面是一个使用JDK动态代理的示例代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 目标接口
interface UserService {
    void saveUser(String username);
}

// 目标对象
class UserServiceImpl implements UserService {
    @Override
    public void saveUser(String username) {
        System.out.println("保存用户:" + username);
    }
}

// 代理处理器
class UserServiceProxy implements InvocationHandler {
    private Object target;

    public UserServiceProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理前");
        Object result = method.invoke(target, args);
        System.out.println("代理后");
        return result;
    }
}

public class JdkDynamicProxyExample {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new UserServiceProxy(target)
        );

        proxy.saveUser("Alice");
    }
}

运行上述代码,可以看到以下输出结果:

代理前
保存用户:Alice
代理后

1.2.2 原理解析

JDK动态代理的原理是基于Java的反射机制。当使用JDK动态代理创建一个代理对象时,JDK会根据传入的目标对象和接口信息,生成一个新的类,在运行时动态加载并生成这个类的字节码,并创建代理对象。代理对象通过实现目标对象的接口,可以调用目标对象的方法,并在调用前后加入自定义的逻辑代码。

JDK动态代理的核心类是java.lang.reflect.Proxy,它提供了静态方法newProxyInstance来创建动态代理对象。Proxy.newProxyInstance方法接受三个参数:ClassLoader、Interfaces、InvocationHandler。通过ClassLoader,可以指定生成代理类的加载器;通过Interfaces,可以指定代理类实现的接口;通过InvocationHandler,可以指定代理类的方法执行时的处理器。

在JDK动态代理中,代理类是通过接口的方式来实现的,因此目标对象必须实现接口才能使用JDK动态代理。

1.2.3 优点与缺点

JDK动态代理的优点是实现简单,无需引入第三方库,使用方便。它可以在运行时动态生成代理类,减少了编码量。另外,JDK动态代理是基于接口的代理,因此适用于对接口进行代理的场景。

然而,JDK动态代理也有一些局限性。首先,目标对象必须实现接口才能使用JDK动态代理,这在某些场景下会限制代理功能的使用。其次,由于JDK动态代理是基于接口的代理,因此不能对类的非公有方法进行代理。最后,JDK动态代理的性能相对较低,生成代理类的过程较为复杂,涉及到类的加载、生成字节码等操作。

1.3 CGLIB动态代理

CGLIB(Code Generation Library)是一个基于ASM(Java字节码操纵框架)的代码生成库,它可以在运行时对目标类进行增强。CGLIB动态代理不需要目标对象实现接口,通过生成目标对象的子类来实现对目标对象的代理访问。

1.3.1 使用案例

下面是一个使用CGLIB动态代理的示例代码:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

// 目标类
class UserService {
    public void saveUser(String username) {
        System.out.println("保存用户:" + username);
    }
}

// 代理拦截器
class UserServiceInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("代理前");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("代理后");
        return result;
    }
}

public class CglibDynamicProxyExample {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new UserServiceInterceptor());
        UserService proxy = (UserService) enhancer.create();

        proxy.saveUser("Bob");
    }
}

运行上述代码,可以看到以下输出结果:

代理前
保存用户:Bob
代理后

1.3.2 原理解析

CGLIB动态代理利用ASM生成目标类的子类,并重写父类中的方法。在代理对象调用方法时,CGLIB通过MethodInterceptor拦截器来拦截方法的调用,并在调用前后加入自定义的逻辑代码。

CGLIB动态代理的核心类是net.sf.cglib.proxy.Enhancer,它通过设置目标类和拦截器来创建代理对象。Enhancer.setSuperclass方法用于指定目标类,Enhancer.setCallback方法用于指定拦截器。

CGLIB动态代理不需要目标对象实现接口,因此对于没有实现接口的类也可以进行代理。

1.3.3 优点与缺点

CGLIB动态代理的主要优点是对目标对象没有任何要求,可以对任意类型的类进行代理,包括没有实现接口的类。另外,CGLIB动态代理不需要引入额外的依赖,使用方便。

然而,CGLIB动态代理也有一些限制。首先,由于CGLIB动态代理是通过生成目标类的子类来实现的,因此目标类不能声明为final。其次,CGLIB动态代理不能对final方法进行代理。最后,CGLIB动态代理的性能相对较低,涉及到类的继承和方法重写。

1.4 小结

JDK动态代理和CGLIB动态代理是常用的代理模式实现方式。它们的区别主要体现在实现原理和使用限制上。

JDK动态代理是基于接口的代理,需要目标对象实现接口才能使用。它通过生成代理类,在运行时动态加载并生成代理类的字节码,实现对目标对象的代理访问。JDK动态代理的优点是实现简单,无需引入第三方库;缺点是功能受限,不能对非公有方法进行代理,性能较低。

CGLIB动态代理不需要目标对象实现接口,通过生成目标对象的子类来实现对目标对象的代理访问。CGLIB动态代理的优点是可以对任意类型的类进行代理,使用方便;缺点是目标类不能声明为final,不能对final方法进行代理,性能较低。

选择使用哪种动态代理方式取决于具体的场景需求和项目要求。

正文到此结束
评论插件初始化中...
Loading...