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


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

相关推荐
简单点了13 分钟前
SM4加密算法
java·开发语言
IT_陈寒20 分钟前
Python开发者必知的5个高效技巧,让你的代码速度提升50%!
前端·人工智能·后端
用户37215742613539 分钟前
Java 实现HTML转Word:从HTML文件与字符串到可编辑Word文档
java
yvya_1 小时前
Mybatis总结
java·spring·mybatis
姜太小白1 小时前
【VSCode】VSCode为Java C/S项目添加图形用户界面
java·c语言·vscode
谦行1 小时前
Andrej Karpathy 谈持续探索最佳大语言模型辅助编程体验之路
后端
一路向北North1 小时前
java将doc文件转pdf
java·pdf
ALex_zry1 小时前
Golang云端编程入门指南:前沿框架与技术全景解析
开发语言·后端·golang
绝无仅有1 小时前
部署 Go 项目的 N 种方法
后端·面试·github