在日常开发中我们常常会遇到这样的需求要在原有的核心业务逻辑比如数学计算中加入一些通用的辅助功能比如打印日志、权限校验。如果直接修改核心代码在里面写满System.out.println()不仅会让代码变得臃肿不堪耦合度极高后续如果日志格式需要变动还会带来极大的维护灾难。为了优雅地解决这个问题Java 引入了经典的设计模式——静态代理模式Static Proxy。本文将带你从零开始通过具体的代码实例拆解彻底搞懂静态代理的运行流向与核心思想。一、 核心架构梳理要实现一个标准的静态代理我们的程序通常需要由四个核心组件构成。理清它们的关系是理解代理模式的第一步接口Interface定义一套规范例如MathCalculator。无论是真正的业务类还是代理类都必须遵守这套规范。目标类Target负责实现真正的核心业务逻辑例如MathCalculatorlmpl。它极其纯粹只包含业务代码。代理类Proxy同样实现该接口。它的核心特点是内部包装了一个目标类并在调用目标类前后插入额外的辅助代码例如CalculatorStaticProxy。客户端/测试类程序的入口负责将目标类和代理类组装起来运行例如MathTest。二、 代码逐级拆解与执行分析下面我们按照开发顺序一步步实现带有日志增强的计算器程序。1. 制定接口规范接口负责声明程序应该具备哪些功能这里我们定义了加减乘除四个方法。// MathCalculator.java package com.example.springaop.calculator; public interface MathCalculator { int add(int a, int b); // 加法 int sub(int a, int b); // 减法 int mul(int a, int b); // 乘法 int div(int a, int b); // 除法 }2. 编写纯粹的核心业务目标类实现上述接口专注于数学计算绝对不掺杂任何日志代码。// MathCalculatorlmpl.java package com.example.springaop.calculator.impl; import com.example.springaop.calculator.MathCalculator; import org.springframework.stereotype.Component; Component public class MathCalculatorlmpl implements MathCalculator { Override public int add(int a, int b) { int result a b; // 纯粹的核心逻辑计算加法 System.out.println(结果result); return result; } // ... sub、mul、div 方法同理省略 }3. 核心魔法编写代理类为了在不修改原目标类的情况下加上日志我们需要一个“包装器”。// CalculatorStaticProxy.java package com.example.springaop.proxy.statics; import com.example.springaop.calculator.MathCalculator; public class CalculatorStaticProxy implements MathCalculator { // 关键点1内部必须持有一个真正的目标对象 private MathCalculator target; // 关键点2通过构造方法把真正的目标对象注入进来 public CalculatorStaticProxy(MathCalculator mc) { this.target mc; } Override public int add(int a, int b) { // 【前置增强】核心计算前打印日志 System.out.println([日志]执行了add方法a,b); // 【核心调用】通过内部引用 target调用真正的计算公式 int add target.add(a, b); // 【后置增强】核心计算后打印日志 System.out.println([日志]add返回add); return add; } }三、 测试运行与控制台输出流向代码写好了它们是如何相互配合的呢我们来看看测试代码及其输出结果。// MathTest.java package com.example.springaop; import org.junit.jupiter.api.Test; public class MathTest { Test void test01() { // --- 对比测试 A无代理模式 --- MathCalculator target new MathCalculatorlmpl(); target.add(1, 2); System.out.println(测试完成...); /* * [情况 A 输出结果] 没有任何日志只有死板的计算。 * 结果3 * 测试完成... */ // --- 对比测试 B静态代理模式 --- // 步骤1创建代理对象把 target 包装进代理对象中 CalculatorStaticProxy proxy new CalculatorStaticProxy(target); // 步骤2客户端不再直接调用 target而是调用代理对象 proxy int add proxy.add(1, 2); System.out.println(add); // 最后打印返回值 /* * [情况 B 输出结果] 完美实现了日志增强代码流向如下 * [日志]执行了add方法1,2 -- 代理类拦截执行【前置增强】 * 结果3 -- 代理类放行目标类执行【真实计算】 * [日志]add返回3 -- 代理类拦截执行【后置增强】 * 3 -- 测试类最后打印最终结果 */ } }四、 总结与进阶思考通过引入静态代理模式我们完美遵守了软件开发中的单一职责原则业务类这辈子只管计算公式。代理类这辈子只负责打印日志。解耦带来的好处是巨大的如果我们今天决定把日志从控制台打印改成写入到本地文件中只需要修改CalculatorStaticProxy即可核心的数学公式代码一行都不需要动极大降低了引发新 Bug 的风险。 引申思考静态代理的“死穴”静态代理虽然优雅但有一个致命缺点——必须手动编写代理类。 试想一下如果你开发的系统中不仅有计算器还有订单系统、用户系统、支付系统共计上百个接口都需要加日志难道我们要手动写上百个像CalculatorStaticProxy这样的代理类吗这显然是不现实的。为了解决这个“类爆炸”的问题Java 演进出了动态代理Dynamic Proxy技术而大名鼎鼎的Spring AOP面向切面编程正是基于此原理能够在程序运行时自动帮我们生成代理对象。彻底解放开发者的双手