﻿---
title: Java 代理模式
date: 2024-10-22
tags:
  - Java
  - Reflection
  - Spring
cover: https://assets.vluv.space/cover/Dev/Java/代理模式.webp
excerpt: "代理模式是一种设计模式，允许一个对象充当另一个对象的替代或中介。代理对象可以控制对目标对象的访问，在不修改原目标对象核心逻辑的情况下，实现额外的功能，如日志记录、权限控制或延迟加载，甚至监控等；"
updated: 2026-05-08 22:34:07
---

<script data-swup-reload-script type="module" src="/js/components/tab.js"></script>

![alt text](https://assets.vluv.space/Java/代理模式/代理模式-1.webp)

**代理模式的应用场景**

- **AOP**: 通过代理类来拦截目标对象方法的调用，实现对目标对象方法的增强。
- **权限校验**：通过代理类来控制对某些方法或资源的访问。
- **日志记录**：在调用目标对象的方法时，记录相关日志信息；
- **性能监控**：在代理类中记录方法的执行时间，便于分析性能瓶颈。
- **懒加载**：通过代理对象延迟初始化某些资源，以节省内存或提高性能。
- **远程代理**：为远程调用提供一个本地代理，使得本地方法调用就像调用远程服务一样。

> [!question]- 为什么引入代理
>
> 通过在代理中实现功能（如日志打印、性能监控），而不是直接修改源代码，能够有效地将功能与业务逻辑解耦
>
> **单一职责原则**：
> 将日志记录、性能监控等功能放在代理中，目标类专注于业务逻辑。这样，业务逻辑的实现和[横切关注点（Cross-Cutting Concerns）](https://en.wikipedia.org/wiki/Cross-cutting_concern)之间的依赖关系被削弱，符合单一职责原则。未来如果日志记录或监控的需求变化，需要改变日志记录的格式、级别或存储方式等，那么只对代理类进行修改即可，而无需改动每个目标类的代码
> **代码复用与一致性**:
> 代码复用：代理类可以被多个目标类共享，避免了在每个目标类中都实现相同的功能（如日志记录），减少冗余代码
> 确保一致性：在所有目标类中应用相同的日志或监控逻辑；所有日志都遵循相同的格式，更易于后期分析和监控
> **易于扩展和修改**
> 功能扩展：如果需要添加新功能（如异常处理、输入验证等），可以在代理类中实现，而无需干扰目标类的业务逻辑。例如，可以在日志记录的基础上添加性能监控的功能，只需扩展 invoke 方法
> 灵活性：可以在运行时动态决定使用哪个代理，例如，可以使用不同的代理实现来添加不同的功能，避免修改目标类，这在团队合作中可以减少冲突。

在 Java 中，代理模式主要分为两种：**静态代理**和**动态代理**。这两种类型的代理虽然都能够控制对目标对象的访问，但它们的实现方式有所不同。

## Static Proxy

假设你有一个接口 `BankAccount`，它定义了 3 个方法。其中一个方法为`withdraw()`

假设有一个实现了这个接口的类 `BankAccountImpl`，它负责具体的取钱操作。现在，你可以创建一个代理类`BankAccountProxy`，它也实现了该接口，但在调用 `BankAccountImpl` 的`withdraw`方法前后，增加一些额外的操作，比如记录日志或进行权限检查。

**Steps**

- 定义接口，`BankAccount Interface`
- 定义 class 实现接口，`BankAccountImpl implements BankAccount`
- 编写代理类，`BankAccountProxy`
- 调用`BankAccountProxy`的构造器，创建静态代理对象实例

<x-tabs>

<x-tab title="AccoutService" active>

```java
package proxydemo;
// 账户服务接口，定义金融操作
public interface BankAccount {
    void viewBalance(); // 查看余额
    void withdraw(); // 取款
    void deposit(); // 存款
}
```

</x-tab>

<x-tab title="BankAccountImpl">

```java
package proxydemo;
// 账户服务实现类
public class BankAccountImpl implements BankAccount {

    @Override
    public void viewBalance() {
        System.out.println("Displaying account balance.");
    }

    @Override
    public void withdraw() {
        System.out.println("Withdrawing funds from account.");
    }

    @Override
    public void deposit() {
        System.out.println("Depositing funds into account.");
    }
}
```

</x-tab>

<x-tab title="BankAccountProxy">

```java
package proxydemo;

// 账户服务代理类
// 代理类负责控制对实际账户服务操作的访问。只有管理员角色才能执行
// withdraw()、deposit()和 transfer()操作，而普通用户只能查看余额
// 和退出服务。

public class BankAccountProxy implements BankAccount {
    private BankAccountImpl BankAccount;
    private String userRole;

    // 构造方法，传入实际的账户服务对象和用户角色
    public BankAccountProxy(BankAccountImpl BankAccount, String userRole) {
        this.BankAccount = BankAccount;
        this.userRole = userRole;
        System.out.println("BankAccountProxy initialized with user role: " + userRole);
    }

    @Override
    public void viewBalance() {
        System.out.println("User with role " + userRole + " is viewing balance.");
        BankAccount.viewBalance(); // 所有用户都可以查看余额
    }

    @Override
    public void withdraw() {
        if ("ADMIN".equals(userRole)) {
            BankAccount.withdraw(); // 只有管理员可以取款
        } else {
            System.err.println("Access denied for user with role " + userRole + ": Only ADMIN can withdraw funds.");
        }
    }

    @Override
    public void deposit() {
        if ("ADMIN".equals(userRole)) {
            BankAccount.deposit(); // 只有管理员可以存款
        } else {
            System.err.println("Access denied for user with role " + userRole + ": Only ADMIN can deposit funds.");
        }
    }
}
```

</x-tab>

<x-tab title="Main">

```java
package proxydemo;

public class Main {
    public static void main(String[] args) {
        BankAccountImpl BankAccount = new BankAccountImpl();

        // 创建管理员用户的代理
        System.out.println("Admin user:");
        BankAccount adminProxy = new BankAccountProxy(BankAccount, "ADMIN");
        adminProxy.viewBalance(); // 管理员可以查看余额
        adminProxy.withdraw(); // 管理员可以取款
        adminProxy.deposit(); // 管理员可以存款

        // 创建普通用户的代理
        System.out.println("\nRegular user:");
        BankAccount userProxy = new BankAccountProxy(BankAccount, "USER");
        userProxy.viewBalance(); // 普通用户可以查看余额
        userProxy.withdraw(); // 普通用户无法取款
        userProxy.deposit(); // 普通用户无法存款
    }
}
```

</x-tab>

</x-tabs>

---

```shell
Admin user:
BankAccountProxy initialized with user role: ADMIN
User with role ADMIN is viewing balance.
Displaying account balance.
Withdrawing funds from account.
Depositing funds into account.

Regular user:
BankAccountProxy initialized with user role: USER
User with role USER is viewing balance.
Displaying account balance.
Access denied for user with role USER: Only ADMIN can withdraw funds.
Access denied for user with role USER: Only ADMIN can deposit funds.
```

**优点**：

- 可以在不修改`BankAccountImpl`类的情况下，添加新的功能；代理类与目标类之间的耦合度低。

**缺点**：

- 如果目标类有很多方法，手动编写代理类会非常繁琐。
- 代理类的代码量较多，且每次更改都需要手动维护。

## Dynamic Proxy

静态代理是在编译期间就需要完成的，即代理类的字节码都已经确定，每个类都要创建一个代理类，导致代码冗余。动态代理不需要手动编写每个代理类，而是在运行时才会生成代理类，并写入 class 文件，相较于静态代理代码冗余更少。

Java 中的动态代理主要有两种方式：

- **JDK 动态代理**：基于接口的代理，它只能代理实现了接口的类。
- **CGLIB 动态代理**：基于继承的代理，它可以代理没有实现接口的类。

### JDK 动态代理

Java JDK 动态代理是基于 `java.lang.reflect.Proxy` 和 `InvocationHandler` 实现的。

- 当调用代理类的方法时，会将其委托给`InvocationHandler`实例的`invoke`方法来运行。
- `Proxy` 类基于目标类实现的接口和 `InvocationHandler` 接口，通过反射机制来创建代理类对象。

JDK 动态代理的原理在这里不赘述，感兴趣可参考[彻底明白 JDK 动态代理的底层原理](https://zhuanlan.zhihu.com/p/616987288)

**Steps**

- 定义接口，`BankAccount Interface`
- 定义 class 实现接口，`BankAccountImpl implements BankAccount`
- 创建`InvocationHandler`接口的实现类
- 使用`java.lang.reflect.Proxy`的`newProxyInstance`方法创建动态代理对象的实例

<x-tabs>

<x-tab title="BankAccount Interface" active>

```java
package proxydemo;
// 账户服务接口，定义金融操作
public interface BankAccount {
    void deposit(double amount);
    void withdraw(double amount);
    double getBalance();
}
```

</x-tab>

<x-tab title="BankAccountImpl">

```java
package proxydemo;

// 账户服务实现类
public class BankAccountImpl implements BankAccount {
    private double balance;

    @Override
    public void deposit(double amount) {
        balance += amount;
        System.out.println("Deposited: " + amount);
    }

    @Override
    public void withdraw(double amount) {
        if (balance >= amount) {
            balance -= amount;
            System.out.println("Withdrew: " + amount);
        } else {
            System.out.println("Insufficient balance");
        }
    }

    @Override
    public double getBalance() {
        return balance;
    }
}
```

</x-tab>

<x-tab title="InvocationHandler">

**定义 JDK 动态代理类**

`InvocationHandler` 是 Java 反射包 java.lang.reflect 中的一个接口，用于处理代理实例的方法调用。

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

public class BankAccountProxyHandler implements InvocationHandler {
    private final BankAccount target;

    public BankAccountProxyHandler(BankAccount target) {
        this.target = target;
    }

    /**
     * 当代理实例上的方法被调用时，这个方法会被触发。它接收三个参数：代理实例、被调用的方法对象和方法参数数组。该方法负责处理方法调用并返回结果
     *
     * @param proxy  代理对象本身。通常在方法中不会直接使用它。
     * @param method 表示正在调用的目标方法。它是一个 Method 对象，封装了该方法的元数据信息（如方法名、参数类型等）。
     * @param args   目标方法调用时传入的实际参数，是一个 Object[] 数组。如果没有参数，则为 null。
     * @return method的返回值（如果有返回值）。
     * @throws Throwable 如果调用目标对象的方法抛出异常，则会向上抛出异常。
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 可以在调用方法之前执行部分操作;
        // 例如，在目标方法调用前添加日志信息,记录执行时间等等
        long startTime = System.currentTimeMillis();
        System.out.println("Method " + method.getName() + " is called with arguments: " + (args != null ? java.util.Arrays.toString(args) : "[]"));
        // 使用 Method 对象调用目标对象（真实对象）的方法，并传递参数 args
        Object result = method.invoke(target, args);
        // 同样地，可以在调用方法之后执行部分操作;
        // 例如，在方法调用后添加日志
        long endTime = System.currentTimeMillis();
        System.out.println("Method " + method.getName() + " executed in " + (endTime - startTime) + " ms");

        // 返回调用结果（如果方法有返回值，则返回该值；如果没有返回值，返回 null）
        return result;
    }
}
```

</x-tab>

<x-tab title="Main">

Proxy 类是 Java 反射 API 中的一个重要类，位于 `java.lang.reflect` 包内。它的主要作用是用来动态创建代理对象，即在运行时生成一个实现特定接口的代理类

`{java} Proxy.newProxyInstance` 是创建动态代理对象的核心方法。它接受三个参数，分别是：

- 类加载器 `ClassLoader`：用于加载代理类的类加载器。通常是目标类的类加载器。
- 接口数组 `{java} Class<?>[]`：指定代理类要实现的接口。代理对象将实现这些接口中的所有方法。
- `InvocationHandler` 实例：一个 InvocationHandler 的实例，它负责处理代理对象的方法调用。

```java
package proxydemo;

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        // 创建银行账户的真实对象
        BankAccount realAccount = new BankAccountImpl();
        // 创建代理对象
        BankAccount proxyAccount = (BankAccount) JdkProxyFactory.getProxy(realAccount);
        // 使用代理对象执行方法
        proxyAccount.deposit(100);
        proxyAccount.withdraw(50);
        System.out.println("Balance: " + proxyAccount.getBalance());
    }
}

class JdkProxyFactory {
    public static Object getProxy(Object target) { // target: real object which implements BankAccount Interface
        // 通过 Proxy 类创建动态代理对象
        // 参数1：类加载器，通常使用目标对象的类加载器
        // 参数2：目标对象实现的接口，可指定多个
        // 参数3：InvocationHandler 实现类
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new BankAccountProxyHandler((BankAccount) target));
    }
}
```

</x-tab>

</x-tabs>

---

```shell
Method deposit is called with arguments: [100.0]
Deposited: 100.0
Method deposit executed in 20 ms
Method withdraw is called with arguments: [50.0]
Withdrew: 50.0
Method withdraw executed in 1 ms
Method getBalance is called with arguments: []
Method getBalance executed in 3 ms
Balance: 50.0
```

### CGLIB 动态代理

[CGLIB(Code Generation Library)](https://mvnrepository.com/artifact/cglib/cglib)依赖于 ASM 库，允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。

![](https://assets.vluv.space/Java/代理模式/代理模式.webp)

> CGLIB 在 Spring 框架中有所应用
> Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. JDK dynamic proxies are built into the JDK, whereas CGLIB is a common open-source class definition library (repackaged into spring-core).
> If the target object to be proxied implements at least one interface, a JDK dynamic proxy is used. All of the interfaces implemented by the target type are proxied. If the target object does not implement any interfaces, a CGLIB proxy is created.

**CGLIB 与 JDK 动态代理的区别**

- 代理对象类型：
  - JDK 动态代理：只能代理实现了接口的类。
  - CGLIB 动态代理：可以代理普通的类（没有实现接口的类）。
- 实现方式：
  - JDK 动态代理：通过反射实现，代理类实现目标对象的接口。
  - CGLIB 动态代理：通过生成目标类的子类来实现。
- 限制：
  - CGLIB 不能代理 final 类和 final 方法，因为它是通过生成子类来实现的，而 final 方法不能被重写。

**Steps**

- 引入 CGLIB 依赖
- 创建目标类,`BankAccount`
- 创建 MethodInterceptor 实现类：处理方法拦截逻辑。
- 生成代理对象：通过 CGLIB 创建代理对象的实例

<x-tabs>

<x-tab title="BankAccount" active>

```java
public class BankAccount {
    public void deposit(double amount) {
        System.out.printf("Depositing: " + amount);
    }

    public void withdraw(double amount) {
        System.out.printf("Withdrawing: " + amount);
    }
}
```

</x-tab>

<x-tab title="MethodInterceptor">

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

import java.lang.reflect.Method;

public class BankAccountInterceptor implements MethodInterceptor {
    /**
     * 拦截器方法
     *
     * @param proxyObj 代理对象
     * @param method 被代理的方法
     * @param args 方法的参数
     * @param proxy 方法的代理对象，用于调用父类的方法
     * @return 方法的返回值
     * @throws Throwable 抛出的异常
     */
    @Override
    public Object intercept(Object proxyObj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Method" + method.getName() + "is being called with arguments: " + java.util.Arrays.toString(args));
        Object res = proxy.invoke(proxyObj, args);
        System.out.println("Method" + method.getName() + "execution completed");
        return res;
    }
}
```

</x-tab>

<x-tab title="Main">

```java
import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {
        // 创建动态代理类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(BankAccount.class);
        enhancer.setCallback(new BankAccountInterceptor());

        // 创建代理对象
        BankAccount proxyAccount = (BankAccount)enhancer.create();
        // 通过代理对象调用方法
        proxyAccount.deposit(100);
        proxyAccount.withdraw(50);
    }
}
```

</x-tab>

</x-tabs>

---

```shell
Method deposit is being called with arguments: [100.0]
Depositing: 100.0
Method deposit execution completed.
Method withdraw is being called with arguments: [50.0]
Withdrawing: 50.0
Method withdraw execution completed.
```

## Ref

[Proxy Java SE 21 & JDK 21](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/reflect/Proxy.html)
