跳至主要內容

Java的动态代理

holic-x...大约 6 分钟碎片化碎片化

Java的动态代理

​ 首先理解代理模式的场景:在调用目标方法的前后做一些增强操作(例如参数校验、日志记录、事务管理等)

​ 代理模式的实现方式有两种,一种是静态代理(通过编写代码实现),一种是动态代理(JDK动态代理:通过反射实现、CGLIB:开源第三方工具库,通过继承实现对目标类的增强

实现方式:JDK动态代理是通过反射实现的(实现InvocationHandler接口及其接口方法),而CGLIB动态代理是通过继承目标类来实现的(如果CGLIB要通过继承方式实现对目标类增强,则目标类和类方法不同用final修饰)

依赖库:JDK动态代理是Java自带的库,不需要额外的依赖,而CGLIB动态代理需要依赖cglib库。

​ 在使用动态代理时,可以根据需要和具体的场景选择合适的实现方式,JDK动态代理适用于接口代理的场景,而CGLIB动态代理适用于类代理的场景

image-20240331084724478

JDK动态代理

场景介绍

​ 所谓代理,就是在目标方法执行前后做一些增强操作。此处以User操作为例,模拟用户的新增、修改前后做一些业务操作

【1】构建核心操作:UserService、UserServiceImpl,模拟addUser、updateUser方法

【2】定义代理实现类UserProxy:实现InvocationHandler方法(实现invoke方法),编写增强的业务逻辑,调用代理对象方法

【3】编写测试类测试代理

【1】定义业务逻辑操作(定义目标类操作)

public interface UserService {
    // 添加用户
    void addUser();
    // 修改用户
    void updateUser(String username);
}

public class UserServiceImpl implements UserService{
    @Override
    public void addUser() {
        System.out.println("添加用户");
    }

    @Override
    public void updateUser(String username) {
        System.out.println("修改用户:"+username);
    }
}

【2】定义代理类

/**
 * 构建一个UserProxy代理类,实现InvocationHandler的invoke方法
 */
public class UserProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }
}
/**
 * 构建一个UserProxy代理类,实现InvocationHandler的invoke方法
 */
public class UserProxy implements InvocationHandler {

    // 定义代理对象
    private Object target;

    // 创建构造函数初始化代理对象
    public UserProxy(Object target){
        this.target = target;
    }

    // 实现代理方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 自定义业务逻辑实现方法增强
        System.out.println("JDK代理增强......before......");

        // 调用代理对象方法
        Object res = method.invoke(target,args);

        System.out.println("JDK代理增强......after......");

        // 返回代理对象
        return res;
    }
}

【3】定义测试类测试代理执行

public class JdkDynamicProxyTest {


    public static void main(String[] args) {
        // 定义目标对象
        UserServiceImpl impl = new UserServiceImpl();
        // 定义代理对象,装载要代理的目标
        UserProxy userProxy = new UserProxy(impl);

        // 装载代理对象
        UserService userService = (UserService) Proxy.newProxyInstance(impl.getClass().getClassLoader(), impl.getClass().getInterfaces(), userProxy);

        // 测试代理方法
        userService.addUser();
        userService.updateUser(":hello noob");
    }
}

image-20240331091627499

CGLIB动态代理

CGLIB动态代理实现步骤

【1】构建核心操作:UserService、UserServiceImpl,模拟addUser、updateUser方法(也可以自定义一个目标类执行一些操作,例如一个普通类UserAction)

【2】定义代理实现类UserServiceCGlib:实现MethodInterceptor方法(实现intercept方法),编写增强的业务逻辑,调用代理对象方法

【3】编写测试类测试代理

<dependencies>
        <!-- 引入CGLIB代理 -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
    </dependencies>

【1】定义业务逻辑操作(定义目标类操作)

/**
 * 定义业务操作类
 */
public class UserAction {
    void doSomething(){
        System.out.println("用户执行操作中.......");
    }
}

【2】定义代理类

/**
 * 定义UserAction代理类,基于CGLIB实现,实现MethodInterceptor
 */
public class UserActionCGlib implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        return null;
    }
}
/**
 * 定义UserAction代理类,基于CGLIB实现,实现MethodInterceptor
 */
public class UserActionCGlib implements MethodInterceptor {

    // 定义代理目标
    private Object target;

    // 构造函数初始化代理目标
    public UserActionCGlib(Object target) {
        this.target = target;
    }

    // 返回一个代理对象: 是 target 对象的代理对象
    public Object getProxyInstance() {
        // 创建一个工具类
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(target.getClass());
        // 设置回调函数
        enhancer.setCallback(this);
        // 创建子类对象,即代理对象
        return enhancer.create();
    }

    // 实现代理方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("===CGLIB增强 before===");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("===CGLIB增强 after===");
        return result;
    }
}

【3】定义测试类测试代理执行

public class UserActionCGlibTest {
    public static void main(String[] args) {
        // 定义代理
        UserActionCGlib userActionCGlib = new UserActionCGlib(new UserAction());
        // 获取代理后的目标对象
        UserAction userAction = (UserAction) userActionCGlib.getProxyInstance();
        // 指定目标方法
        userAction.doSomething();
    }
}

image-20240331093330833

JDK动态代理 VS CGLIB动态代理

JDK动态代理 VS CGLIB动态代理

动态代理方式JDK动态代理CGLIB动态代理
代理对象代理实现了接口的类可代理没有实现接口的类,通过继承目标类生成子类的方式来创建代理对象
实现机制实现 InvocationHandler 接口实现 MethodInterceptor 接口
性能差异基于反射机制实现代理,调用性能不如CGLIB直接操作字节码生成新的类,避免反射的开销
使用场景适用于接口驱动的代理场景,不涉及具体类、只关心接口定义时适用可代理没有实现接口的类,通过继承增强功能的场景适用
关联依赖Java核心API的一部分,不需引入任何依赖需引入第三方cglib包
扩展性、复杂性使用简单提供更多的控制(方法拦截、回调等),使用复杂

​ 当使用JDK动态代理时,Java运行时会动态生成代理类的字节码,并直接加载到JVM。这个过程是在内存中完成的,通常不会输出为磁盘上的字节码文件(即.class文件)。JDK动态代理使用java.lang.reflect.Proxy类的newProxyInstance方法来在运行时动态创建代理对象。其会根据传入的接口信息,使用反射机制生成一个实现了指定接口的代理类,并将其加载到JVM中

​ CGLIB(Code Generation Library)也会在运行时生成新的类的字节码,但它是通过操作底层字节码(使用第三方库如ASM)来创建新的类。与JDK动态代理类似,CGLIB通常会将生成的字节码直接加载到JVM中,而不是写入到文件系统。CGLIB是通过其Enhancer类来生成新的代理对象,这些对象是原始类的子类。

Spring框架的选择

​ Spring框架优选选择JDK动态代理模式,主要是基于其兼容性(JDK动态代理是Java核心的一部分,不依赖第三方JAR)、简洁性、避免类加载问题(CGLIB会为目标对象创建一个子类,在一些复杂的类加载环境可能存在一点问题)

JDK动态代理CGLIB动态代理都被广泛地应用在各种Java框架中。Spring可以根据情况选择使用JDK动态代理还是CGLIB动态代理。默认情况下,Spring会优先使用JDK动态代理,如果要代理的对象没有实现接口,则会使用CGLIB动态代理

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3