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 框架的核心技术之一。


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

相关推荐
leobertlan2 小时前
2025年终总结
前端·后端·程序员
面向Google编程2 小时前
从零学习Kafka:数据存储
后端·kafka
易安说AI3 小时前
Claude Opus 4.6 凌晨发布,我体验了一整晚,说说真实感受。
后端
易安说AI3 小时前
Ralph Loop 让Claude无止尽干活的牛马...
前端·后端
易安说AI3 小时前
用 Claude Code 远程分析生产日志,追踪 Claude Max 账户被封原因
后端
JH30733 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
颜酱4 小时前
图结构完全解析:从基础概念到遍历实现
javascript·后端·算法
Coder_Boy_4 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
invicinble5 小时前
对tomcat的提供的功能与底层拓扑结构与实现机制的理解
java·tomcat
较真的菜鸟5 小时前
使用ASM和agent监控属性变化
java