← 返回题目列表

什么是静态代理?它有什么优缺点?

高频 简单 第 2 / 25 题 更新于 2026/07/28
代理模式静态代理Java结构型模式

简化版

静态代理是在编译期手写代理类,让代理类和目标类实现同一个接口,并在代理类中调用目标对象。它优点是简单直观,缺点是每个接口或方法都可能要写代理类,扩展成本高。

详细版

静态代理通常有三个角色:

  • 接口:代理和目标共同实现;
  • 目标类:真正执行业务;
  • 代理类:持有目标对象,并在调用前后增强。

示例:

interface OrderService {
    void createOrder();
}

class OrderServiceImpl implements OrderService {
    public void createOrder() {
        System.out.println("创建订单");
    }
}

class OrderServiceProxy implements OrderService {
    private final OrderService target;

    OrderServiceProxy(OrderService target) {
        this.target = target;
    }

    public void createOrder() {
        System.out.println("开启事务");
        target.createOrder();
        System.out.println("提交事务");
    }
}

优点:

  • 代码直观,容易理解;
  • 不依赖反射和字节码增强;
  • 编译期类型安全;
  • 适合少量接口、固定增强逻辑。

缺点:

  • 每个被代理类都要写代理类;
  • 接口方法变动时,代理类也要跟着改;
  • 代理逻辑重复,维护成本高;
  • 不适合大规模横切增强。

所以静态代理适合理解代理模式本质,真实项目里大规模增强通常会使用动态代理或 AOP。

完整版教学

一、静态代理为什么叫“静态”

“静态”指代理类在编译前就已经写好了。也就是说,源码里真实存在一个 OrderServiceProxy 类。

它和动态代理的区别是:动态代理的代理类通常在运行期生成,而静态代理是开发者手写的。

静态代理的调用链很清楚:

客户端 -> OrderServiceProxy -> OrderServiceImpl

客户端面向接口:

OrderService service = new OrderServiceProxy(new OrderServiceImpl());
service.createOrder();

这段代码的好处是调用方不需要知道增强细节,只要调用 OrderService 接口即可。

二、静态代理解决了什么问题

假设没有代理,事务逻辑可能写在业务方法里:

public void createOrder() {
    beginTransaction();
    doCreateOrder();
    commit();
}

这样业务方法混入了事务控制。多个方法都要事务时,会出现大量重复代码。

静态代理可以把事务逻辑移到代理类:

public void createOrder() {
    beginTransaction();
    target.createOrder();
    commit();
}

真实对象只做业务:

public void createOrder() {
    System.out.println("创建订单");
}

这就是代理模式的典型价值:把横切逻辑从业务类中拆出来。

三、静态代理的结构为什么要求共同接口

代理对象要让客户端“感觉像真实对象”,所以通常要和真实对象实现同一个接口。

class OrderServiceProxy implements OrderService
class OrderServiceImpl implements OrderService

这样客户端可以写:

OrderService service = proxy;

如果没有共同接口,客户端就要知道具体代理类和真实类的差异,代理的透明性就差了。

共同接口也让代理能够替换真实对象,这是面向接口编程的重要体现。

四、静态代理的主要问题

静态代理的问题不是“不能用”,而是“不适合规模化”。

如果有 100 个 Service,每个 Service 都要加日志,你难道手写 100 个 Proxy 类吗?

如果接口新增一个方法,代理类也要补这个方法。否则编译不过,或者增强逻辑缺失。

比如:

interface OrderService {
    void createOrder();
    void cancelOrder();
}

代理类也必须实现两个方法。接口越多,重复越多。

所以静态代理的核心缺点是代理类数量和维护成本随业务规模增长。

五、静态代理适合什么场景

静态代理适合:

  • 代理对象数量少;
  • 代理逻辑比较稳定;
  • 希望代码显式、易调试;
  • 教学或理解代理模式原理;
  • 对性能和可控性要求较高的小范围增强。

如果要给大量类统一加事务、日志、权限,动态代理或 AOP 更合适。

六、静态代理的编译期结构

若 8 个服务各有 4 个方法,逐个手写日志代理可能产生 32 个转发方法;接口变化还要同步修改真实类和代理类。

compile -> UserServiceProxy.class -> target.createUser()

这个推演把模式带来的收益和成本放在同一条调用链上。设计评审时既要确认结果正确,也要确认额外层次没有改变原有契约。

七、边界、代价与验证

检查维度应确认的内容
正确性“静态”指代理类在编译期已经存在,不是指它只能代理 static 方法。
适用边界静态代理适合接口少、增强规则明确的场景;横切面覆盖几十个接口时,维护成本会快速增长。
测试证据覆盖正常路径、异常路径、空值或未知类型,并断言调用次数与顺序
运行成本记录额外对象、调用层数、延迟、线程或内存开销,不用模式名称代替测量

模式落地后要用单元测试验证独立职责,用集成测试验证对象装配和真实调用入口。涉及并发、远程或异步时,还要补充竞态、超时、重复执行与资源释放测试。

易错点:“静态”指代理类在编译期已经存在,不是指它只能代理 static 方法。

八、常见误区与追问

  • 误区:静态代理只能增强静态方法。 这里的静态描述生成时机;实例方法同样可以由手写代理转发。
  • 误区:代理类存在,就代表所有调用都会经过代理。 只有客户端持有并调用代理引用时增强才生效;绕过代理直接调用目标对象,调用链自然不会出现增强。
  • 误区:示例代码能运行,就代表模式边界设计正确。 能运行只证明一条路径,仍要检查异常、并发、顺序、生命周期以及新增实现时是否修改稳定代码。
  • 追问:静态代理比动态代理更落后吗? 不是;它类型直观、易调试,在小范围或需要精确控制时反而更合适。
  • 追问:代理模式和装饰器模式能只靠类图区分吗? 不能;两者都可能包装同一接口,必须结合“控制访问”还是“叠加职责”的设计意图区分。
  • 追问:怎样验证这套设计没有改变原有语义? 先做基线行为测试,再对新增层验证返回值、异常类型、调用次数和顺序;性能敏感处还要比较改造前后的量化指标。

九、加强记忆

静态代理就是手写一个“中间人”:它和目标类实现同一个接口,调用目标前后加增强。它清楚、稳定、好理解,但类多了会很累,所以是理解代理模式的入口,不是大规模横切增强的最佳方案。