一文搞懂 Java 中的动态代理

你在 Java 后端开发过程中,有没有遇到过这样的困扰:想在不修改原有代码的基础上,给对象的方法增加一些额外功能,比如记录日志、进行权限检查或者事务管理?要是手动一个个去添加,那工作量可太大了,而且代码的可维护性也会变得很差。其实,Java 中的动态代理机制就能完美解决这类问题,它能让你在运行时动态创建代理类和对象,实现对原有对象行为的增强或改变。那动态代理到底是什么,又该如何实现呢?今天咱就来好好唠唠。

动态代理是个啥

动态代理,简单来说,就是在程序运行的时候,动态生成代理类和实例的一种机制。打个比方,你开了一家网店卖衣服,你自己就是那个 “真实对象”,每天要处理各种订单、发货等事情。但随着生意越来越好,你忙不过来了,就请了个助手。这个助手就是 “代理对象”,客户找你买衣服,其实都是通过助手来沟通的,助手在处理客户需求的过程中,比如在你发货之前,帮你检查一下衣服有没有瑕疵,这就是在调用真实对象(你)的方法之前,插入了额外的行为。

从技术角度讲,动态代理的核心思想就是用一个代理类包装真实对象,在调用真实对象的方法时,能够执行一些额外操作。在 Java 编程里,动态代理应用可广泛了,在 AOP(面向切面编程)领域,能在方法调用前后添加像日志记录、性能监控这类通用功能;在远程方法调用(RMI)场景中,客户端通过代理对象就能调用远程服务器上的方法;在安全控制方面,能在方法调用前检查权限,只有授权用户才能执行某些操作;在事务管理中,能在方法调用前后开启和关闭数据库事务。

动态代理的实现原理

Java 实现动态代理主要有两种方式,一种是利用 JDK 自带的 Proxy 类,另一种是借助第三方库 CGLIB 。

JDK 动态代理

定义接口:首先得定义一个接口,这个接口里包含了你需要代理的方法。比如,你有一个用户管理的功能,那可以定义一个 UserService 接口,里面有添加用户、删除用户等方法。

public interface UserService {
    void addUser(String username);
    void deleteUser(String username);
}

实现 InvocationHandler 接口:创建一个类,实现 InvocationHandler 接口。这个接口里有个 invoke 方法,当代理对象调用方法时,实际会调用到这个 invoke 方法,在这里你可以编写代理逻辑。比如说,你想在调用添加用户方法时记录日志,就可以在 invoke 方法里实现这个功能。

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

public class UserServiceInvocationHandler implements InvocationHandler {
    private Object target;

    public UserServiceInvocationHandler(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;
    }
}

创建代理对象:使用 Proxy 类的静态方法 newProxyInstance () 来创建代理对象。这个方法需要三个参数,分别是被代理类的类加载器,被代理类实现的接口列表,以及前面实现的 InvocationHandler 接口的实例。通过这个方法,就能得到一个代理对象,它实现了定义的接口,并且在调用接口方法时,会执行我们在 invoke 方法里编写的逻辑 。

import java.lang.reflect.Proxy;

public class ProxyFactory {
    public static Object createProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new UserServiceInvocationHandler(target)
        );
    }
}

CGLIB 动态代理

引入 CGLIB 依赖:如果你要用 CGLIB 实现动态代理,首先要在项目里引入 CGLIB 库。在 Maven 项目中,可以添加如下依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

实现 MethodInterceptor 接口:创建一个类实现 MethodInterceptor 接口,在里面定义方法调用的拦截逻辑。和 JDK 动态代理的 InvocationHandler 接口类似,不过它是 CGLIB 库特有的。

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

import java.lang.reflect.Method;

public 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;
    }
}

创建代理对象:使用 Enhancer 类的 create () 方法来创建代理对象。在创建时,需要设置被代理类以及前面实现的 MethodInterceptor 接口的实例 。

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyFactory {
    public static Object createProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new UserServiceInterceptor());
        return enhancer.create();
    }
}

CGLIB 和 JDK 动态代理的区别在于,JDK 动态代理要求被代理对象必须实现接口,因为代理类是通过实现相同接口来代理的;而 CGLIB 是通过生成被代理类的子类来实现代理,所以不需要被代理对象实现接口。不过,如果被代理类是 final 类,CGLIB 就无法代理了,因为 final 类不能有子类 。

动态代理的应用场景

AOP 编程

在 AOP 编程中,动态代理是实现切面功能的重要手段。比如,在一个电商系统里,你可能需要在所有业务方法执行前后记录日志,传统做法是在每个业务方法里手动添加日志记录代码,这样不仅繁琐,而且后期维护也麻烦。但通过动态代理,你只需要编写一个通用的日志记录逻辑,利用动态代理将这个逻辑织入到各个业务方法中,代码简洁又易于维护。

事务管理

在涉及数据库操作的应用中,事务管理至关重要。通过动态代理,在调用数据库操作方法前开启事务,方法执行结束后根据执行结果提交或回滚事务,确保数据的一致性和完整性。比如在一个银行转账的业务场景中,通过动态代理保证转账操作的原子性,要么都成功,要么都失败 。

远程方法调用

在分布式系统中,客户端调用远程服务器上的方法时,动态代理可以将本地的方法调用转换为网络请求,发送到远程服务器执行,并将结果返回给客户端。对客户端来说,就像调用本地方法一样方便,屏蔽了网络通信的复杂性 。

权限控制

在一些系统中,不同用户有不同的操作权限。通过动态代理,在方法调用前检查当前用户是否有权限执行该操作,如果没有权限则阻止调用,保障系统的安全性。例如在一个企业管理系统中,普通员工可能不能删除重要数据,通过动态代理进行权限检查,就能防止这类越权操作 。

总结

Java 中的动态代理机制为后端开发人员提供了强大的功能扩展能力,通过在运行时动态生成代理类和对象,能轻松实现对原有对象行为的增强和改变,广泛应用于各种开发场景中。如果你还没掌握动态代理,那可得抓紧时间学习学习,它能让你的代码开发效率和质量都更上一层楼。各位互联网大厂的后端开发小伙伴们,赶紧动手实践起来,在项目中运用动态代理优化你的代码吧!要是在学习或使用过程中有什么问题,欢迎在评论区留言,咱们一起交流探讨,共同进步。

原文链接:,转发请注明来源!