Java 动态代理详解:从原理到实战

Java 动态代理详解:从原理到实战

1. 什么是代理?

在软件开发中,代理模式(Proxy Pattern) 是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。

简单来说:代理就是帮别人做事的人

比如,你想买演唱会门票,可以自己去买(真实对象),也可以找黄牛帮你买(代理对象)。

在 Java 中,代理有两种:

  • 静态代理:代理类在编译期就已经生成。
  • 动态代理:代理类在运行时动态生成。

今天我们重点讲 动态代理


2. 为什么需要动态代理?

假设我们有一个接口 UserService,有多个方法,比如:

java 复制代码
public interface UserService {
    void createUser(String name);
    void deleteUser(String name);
}

如果我们想在每个方法执行前后加上 日志、权限验证 等功能,

静态代理需要手动写一个代理类,方法多了就很麻烦。

而动态代理可以让我们一次性编写通用逻辑,在运行时帮我们生成代理类,省时省力。


3. Java 动态代理的原理

Java 动态代理依赖 java.lang.reflect.Proxy 类和 InvocationHandler 接口。

核心思想:

  1. 接口:被代理的对象必须实现接口(JDK 动态代理的限制)。
  2. InvocationHandler:定义代理逻辑(方法调用前后做什么)。
  3. Proxy.newProxyInstance:运行时生成代理对象。

执行流程:

rust 复制代码
方法调用 -> 代理对象 -> InvocationHandler.invoke() -> 真实对象

4. 动态代理示例

4.1 接口与实现类

java 复制代码
public interface UserService {
    void createUser(String name);
    void deleteUser(String name);
}

public class UserServiceImpl implements UserService {
    @Override
    public void createUser(String name) {
        System.out.println("创建用户:" + name);
    }

    @Override
    public void deleteUser(String name) {
        System.out.println("删除用户:" + name);
    }
}

4.2 动态代理处理器

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LoggingInvocationHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[日志] 方法开始:" + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("[日志] 方法结束:" + method.getName());
        return result;
    }
}

4.3 创建代理对象

java 复制代码
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();

        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new LoggingInvocationHandler(target)
        );

        proxy.createUser("Alice");
        proxy.deleteUser("Bob");
    }
}

运行结果:

css 复制代码
[日志] 方法开始:createUser
创建用户:Alice
[日志] 方法结束:createUser
[日志] 方法开始:deleteUser
删除用户:Bob
[日志] 方法结束:deleteUser

classDiagram class UserService { <> +createUser(String name) +deleteUser(String name) } class UserServiceImpl { +createUser(String name) +deleteUser(String name) } class LoggingInvocationHandler { -Object target +invoke(Object proxy, Method method, Object[] args) Object } class Proxy { +newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) Object } class InvocationHandler { <> +invoke(Object proxy, Method method, Object[] args) Object } UserService <|.. UserServiceImpl InvocationHandler <|.. LoggingInvocationHandler Proxy --> InvocationHandler Proxy --> UserService LoggingInvocationHandler --> UserService

5. 动态代理的优缺点

优点:

  • 通用性强:一套代理逻辑可以应用到多个类。
  • 解耦:业务代码与横切逻辑(如日志、事务)分离。
  • 运行时生成:无需手动编写代理类。

缺点:

  • 只能代理接口(JDK 动态代理的限制)。
  • 反射调用有一定性能损耗(一般可忽略)。

6. 扩展:CGLIB 动态代理

如果目标类没有实现接口 ,JDK 动态代理就无能为力,这时可以用 CGLIB (基于继承生成子类代理)。

Spring AOP 就是根据情况选择 JDK 动态代理或 CGLIB。


7. 总结

Java 动态代理本质上是 在运行时生成代理类,通过反射调用目标对象的方法 ,并在调用前后插入额外逻辑。

它是实现 AOP(面向切面编程) 的重要基础,也是 Spring 框架的核心技术之一。


💡 建议:如果你在项目中需要对多个类的方法统一添加日志、权限、事务等功能,可以优先考虑动态代理,而不是在每个方法里手动写重复代码。

相关推荐
CodeSheep1 分钟前
JetBrains官宣,又一个IDE可以免费用了!
前端·后端·程序员
刘新明19893 分钟前
Frida辅助分析OLLVM虚假控制流程(下)
java·开发语言·前端
第二只羽毛25 分钟前
重载和继承的实践
java·开发语言
间彧30 分钟前
SpringBoot和Servlet的联系
后端
王嘉俊92530 分钟前
设计模式--适配器模式:优雅解决接口不兼容问题
java·设计模式·适配器模式
间彧31 分钟前
Spring Boot的DispatcherServlet是如何封装和扩展原生Servlet功能的?
后端
王嘉俊92531 分钟前
设计模式--组合模式:统一处理树形结构的优雅设计
java·设计模式·组合模式
无名之辈J33 分钟前
GC Overhead 的排查
后端
道199339 分钟前
50 台小型无人车与50套穿戴终端 5 公里范围内通信组网方案深度研究
java·后端·struts
迎風吹頭髮44 分钟前
UNIX下C语言编程与实践35-UNIX 守护进程编写:后台执行、脱离终端、清除掩码与信号处理
java·c语言·unix