Skip to content

阶段八:进阶与调优(持续学习) ​

作为资深前端开发者,你已经具备了扎实的工程基础。本阶段将深入学习JVM原理、性能优化、架构设计和源码阅读,帮助你从"会用"进阶到"理解本质",成为真正的高级Java开发者。


8.1 JVM深入(第1-2周) ​

8.1.1 JVM内存模型 ​

JVM(Java Virtual Machine)内存模型是Java程序运行的基础,理解它对于性能调优和问题排查至关重要。

内存区域划分 ​

┌─────────────────────────────────────────────────────────┐
│                      JVM 内存结构                        │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌─────────────────────┐  ┌─────────────────────────┐  │
│  │    线程私有区域       │  │      线程共享区域         │  │
│  │                     │  │                         │  │
│  │  ┌───────────────┐  │  │  ┌───────────────────┐  │  │
│  │  │  程序计数器     │  │  │  │      堆(Heap)    │  │  │
│  │  │  (PC Register) │  │  │  │  ┌─────────────┐  │  │  │
│  │  └───────────────┘  │  │  │  │ 新生代(Young) │  │  │  │
│  │  ┌───────────────┐  │  │  │  │  ├─Eden区     │  │  │  │
│  │  │  虚拟机栈      │  │  │  │  │  ├─Survivor0 │  │  │  │
│  │  │  (VM Stack)   │  │  │  │  │  └─Survivor1 │  │  │  │
│  │  │  ├─栈帧        │  │  │  │  └─────────────┘  │  │  │
│  │  │  │ ├─局部变量表│  │  │  │  ┌─────────────┐  │  │  │
│  │  │  │ ├─操作数栈  │  │  │  │  │ 老年代(Old)  │  │  │  │
│  │  │  │ └─动态链接  │  │  │  │  └─────────────┘  │  │  │
│  │  │  └─────────────│  │  │  └───────────────────┘  │  │
│  │  └───────────────┘  │  │  ┌───────────────────┐  │  │
│  │  ┌───────────────┐  │  │  │   方法区           │  │  │
│  │  │  本地方法栈    │  │  │  │  (Method Area)    │  │  │
│  │  │ (Native Stack)│  │  │  │  ├─类信息          │  │  │
│  │  └───────────────┘  │  │  │  ├─常量池          │  │  │
│  │                     │  │  │  ├─静态变量         │  │  │
│  │                     │  │  │  └─JIT编译代码      │  │  │
│  │                     │  │  └───────────────────┘  │  │
│  └─────────────────────┘  └─────────────────────────┘  │
│                                                         │
│  ┌─────────────────────────────────────────────────┐    │
│  │              直接内存(Direct Memory)             │    │
│  │              (NIO直接分配的堆外内存)              │    │
│  └─────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────┘

与前端对比:

前端概念JVM概念说明
V8堆内存JVM堆对象分配的主要区域
调用栈(Call Stack)虚拟机栈方法调用的栈帧管理
全局作用域方法区存储类信息和静态变量
Chrome内存限制JVM内存参数可通过参数调整上限
Web Worker线程JVM线程每个线程有独立的栈空间

堆(Heap)详解 ​

堆是JVM中最大的内存区域,用于存储所有对象实例。

java
/**
 * 堆内存分配示例
 * 通过JVM参数观察堆内存变化
 */
public class HeapDemo {
    
    public static void main(String[] args) {
        // 查看堆内存信息
        Runtime runtime = Runtime.getRuntime();
        long maxMemory = runtime.maxMemory();       // 最大堆内存
        long totalMemory = runtime.totalMemory();   // 当前堆内存
        long freeMemory = runtime.freeMemory();     // 空闲内存
        
        System.out.println("最大堆内存: " + maxMemory / 1024 / 1024 + " MB");
        System.out.println("当前堆内存: " + totalMemory / 1024 / 1024 + " MB");
        System.out.println("可用空闲内存: " + freeMemory / 1024 / 1024 + " MB");
        
        // 对象在堆中的分配
        // 所有new出来的对象都在堆中
        byte[] data1 = new byte[10 * 1024 * 1024];  // 分配10MB
        System.out.println("分配10MB后空闲: " + runtime.freeMemory() / 1024 / 1024 + " MB");
        
        byte[] data2 = new byte[50 * 1024 * 1024];  // 分配50MB
        System.out.println("分配50MB后空闲: " + runtime.freeMemory() / 1024 / 1024 + " MB");
        
        // 触发GC
        data1 = null;
        data2 = null;
        System.gc();
        System.out.println("GC后空闲: " + runtime.freeMemory() / 1024 / 1024 + " MB");
    }
}

堆的分代结构:

堆内存分代模型:

新生代(Young Generation)—— 占堆的1/3
├── Eden区(80%)           —— 新对象首先分配在这里
├── Survivor 0(10%)       —— Minor GC后存活的对象
└── Survivor 1(10%)       —— 与S0交替使用

老年代(Old Generation)—— 占堆的2/3
└── 长期存活的对象(经过多次GC仍然存活)
    ├── 大对象直接进入老年代
    └── 年龄达到阈值的对象(默认15岁)

虚拟机栈(VM Stack)详解 ​

每个线程创建时都会分配一个虚拟机栈,存储方法调用的栈帧。

java
/**
 * 虚拟机栈示例
 * 每个方法调用创建一个栈帧
 */
public class StackDemo {
    
    public static void main(String[] args) {
        methodA();
    }
    
    public static void methodA() {
        int a = 1;           // 局部变量表
        int b = 2;
        methodB(a, b);
    }
    
    public static void methodB(int x, int y) {
        int sum = x + y;
        methodC(sum);
    }
    
    public static void methodC(int value) {
        System.out.println("结果: " + value);
        // 此时调用栈为:methodC -> methodB -> methodA -> main
    }
}

/*
 * 栈帧结构(以methodB为例):
 * ┌─────────────────────────┐
 * │  局部变量表              │
 * │  [0] x = 1              │
 * │  [1] y = 2              │
 * │  [2] sum = 3            │
 * ├─────────────────────────┤
 * │  操作数栈               │
 * │  [  ]                   │
 * ├─────────────────────────┤
 * │  动态链接 → 方法区引用    │
 * ├─────────────────────────┤
 * │  返回地址 → methodA下一行 │
 * └─────────────────────────┘
 */

栈溢出示例:

java
/**
 * StackOverflowError 示例
 * 类比前端:递归组件没有终止条件导致栈溢出
 */
public class StackOverflowDemo {
    
    // 无限递归 —— 类比前端无限递归渲染
    public static void infiniteRecursion() {
        infiniteRecursion();  // 每次调用都会在栈中新增一个栈帧
    }
    
    public static void main(String[] args) {
        try {
            infiniteRecursion();
        } catch (StackOverflowError e) {
            System.out.println("栈溢出!");
            // 类似前端的 "Maximum call stack size exceeded"
        }
    }
}

方法区(Method Area)详解 ​

方法区存储类的元数据信息、常量池、静态变量等。

java
/**
 * 方法区存储内容示例
 */
public class MethodAreaDemo {
    
    // 静态变量存储在方法区
    private static String staticField = "静态变量";
    
    // 常量存储在方法区(运行时常量池)
    private static final String CONSTANT = "常量";
    
    // 类信息存储在方法区
    static {
        System.out.println("静态代码块,类加载时执行");
    }
    
    public static void main(String[] args) {
        // 反射获取类信息(这些信息存储在方法区)
        Class<?> clazz = MethodAreaDemo.class;
        
        System.out.println("类名: " + clazz.getName());
        System.out.println("字段数量: " + clazz.getDeclaredFields().length);
        System.out.println("方法数量: " + clazz.getDeclaredMethods().length);
    }
}

/*
 * 方法区在JDK不同版本的变化:
 * JDK 7及之前:永久代(PermGen),使用JVM自己的堆内存
 * JDK 8及之后:元空间(Metaspace),使用操作系统本地内存
 *
 * 前端对比:
 * 方法区类似于JavaScript引擎中的"全局环境"
 * 存储了函数定义、全局变量等元信息
 */

8.1.2 垃圾回收算法与收集器 ​

如何判断对象可以回收 ​

1. 引用计数法(简单但有循环引用问题)

java
/**
 * 引用计数法问题:循环引用
 * JavaScript使用"标记-清除"解决此问题
 */
public class ReferenceCountDemo {
    private Object partner;  // 互相引用
    
    public static void main(String[] args) {
        ReferenceCountDemo a = new ReferenceCountDemo();
        ReferenceCountDemo b = new ReferenceCountDemo();
        
        a.partner = b;
        b.partner = a;
        
        // a和b的引用计数都为1
        // 即使a和b都不再被外部使用,引用计数也不为0
        // 这就是循环引用问题
        a = null;
        b = null;
        // 引用计数法无法回收,但可达性分析可以
    }
}

2. 可达性分析算法(JVM实际使用)

java
/**
 * 可达性分析算法
 * 从GC Roots出发,不可达的对象即为垃圾
 * 
 * GC Roots包括:
 * 1. 虚拟机栈中引用的对象(局部变量)
 * 2. 方法区中静态属性引用的对象
 * 3. 方法区中常量引用的对象
 * 4. 本地方法栈中引用的对象
 * 5. 被同步锁(synchronized)持有的对象
 */
public class ReachabilityDemo {
    
    // 静态变量是GC Root
    private static Object gcRoot;
    
    public static void main(String[] args) {
        Object obj = new Object();  // 局部变量是GC Root可达
        gcRoot = obj;               // 通过GC Root可达,不会被回收
        
        obj = null;                 // 局部变量不再引用
        // 但gcRoot仍然引用,所以对象不会被回收
        
        gcRoot = null;              // GC Root不再引用
        // 对象变为不可达,下次GC时会被回收
    }
}

四种引用类型 ​

java
import java.lang.ref.*;

public class ReferenceDemo {
    
    public static void main(String[] args) {
        // 1. 强引用(Strong Reference)—— 最常见,不会被回收
        Object strongRef = new Object();
        
        // 2. 软引用(Soft Reference)—— 内存不足时回收
        // 适合做缓存
        SoftReference<Object> softRef = new SoftReference<>(new Object());
        Object cached = softRef.get();  // 可能返回null(已被回收)
        
        // 3. 弱引用(Weak Reference)—— 下次GC时回收
        // 适合做观察者模式等临时引用
        WeakReference<Object> weakRef = new WeakReference<>(new Object());
        Object weakObj = weakRef.get();  // 可能返回null
        
        // 4. 虚引用(Phantom Reference)—— 随时可能回收
        // 用于跟踪对象被回收的活动
        ReferenceQueue<Object> queue = new ReferenceQueue<>();
        PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
        
        // 实际使用:WeakHashMap
        WeakHashMap<String, Object> weakMap = new WeakHashMap<>();
        weakMap.put("key", new Object());
        // 当key不再被外部强引用时,整个entry会被回收
    }
}

垃圾回收算法 ​

1. 标记-清除(Mark-Sweep)
   ┌───┬───┬───┬───┬───┬───┬───┐
   │ 用│ 垃圾│ 用│ 垃圾│ 垃圾│ 用│ 用│
   └───┴───┴───┴───┴───┴───┴───┘
   标记后:
   ┌───┬───┬───┬───┬───┬───┬───┐
   │ ✓ │ ✗ │ ✓ │ ✗ │ ✗ │ ✓ │ ✓ │
   └───┴───┴───┴───┴───┴───┴───┘
   清除后(产生碎片):
   ┌───┬   ┬───┬   ┬   ┬───┬───┐
   │ ✓ │   │ ✓ │   │   │ ✓ │ ✓ │
   └───┴───┴───┴───┴───┴───┴───┘
   缺点:内存碎片

2. 标记-复制(Copying)
   From区:┌───┬───┬───┬───┐    To区:┌───┬───┬───┬───┐
          │ 用│垃圾│ 用│垃圾│         │   │   │   │   │
          └───┴───┴───┴───┘         └───┴───┴───┴───┘
   复制存活对象:
   From区:┌───┬───┬───┬───┐    To区:┌───┬───┬───┬───┐
          │ 用│垃圾│ 用│垃圾│         │ 用│ 用│   │   │
          └───┴───┴───┴───┘         └───┴───┴───┴───┘
   优点:无碎片  缺点:浪费一半空间

3. 标记-整理(Mark-Compact)
   清除后整理:
   ┌───┬───┬───┬───┬   │   │   │
   │ ✓ │ ✓ │ ✓ │   │   │   │   │
   └───┴───┴───┴───┴───┴───┴───┘
   优点:无碎片,空间利用率高  缺点:移动对象开销大

4. 分代收集(Generational Collection)
   新生代 → 标记-复制(对象存活率低,复制成本小)
   老年代 → 标记-清除 或 标记-整理

垃圾收集器 ​

CMS收集器(Concurrent Mark Sweep)—— 追求最短停顿时间

CMS工作流程:
1. 初始标记(STW)     → 标记GC Roots直接关联的对象,速度快
2. 并发标记             → 与用户线程并发执行,遍历对象图
3. 重新标记(STW)     → 修正并发标记期间的变化
4. 并发清除             → 与用户线程并发执行,清除垃圾

STW = Stop The World(暂停所有用户线程)

优点:低停顿时间
缺点:
  - 对CPU资源敏感(并发阶段占用CPU)
  - 无法处理浮动垃圾(重新标记后新产生的垃圾)
  - 内存碎片(使用标记-清除算法)

G1收集器(Garbage First)—— JDK 9默认收集器

G1将堆划分为多个大小相等的Region:

┌───┬───┬───┬───┬───┬───┬───┬───┐
│ E │ E │ O │ O │ E │ H │ E │ O │
└───┴───┴───┴───┴───┴───┴───┴───┘
E = Eden   O = Old   H = Humongous(大对象)

G1工作流程:
1. 初始标记(STW)
2. 并发标记
3. 最终标记(STW)
4. 筛选回收(STW)—— 选择回收价值最高的Region优先回收

特点:
  - 可预测的停顿时间(通过-XX:MaxGCPauseMillis设置)
  - 整体使用标记-整理,局部使用标记-复制
  - 适合大堆内存(6GB+)

ZGC收集器(JDK 15正式发布)—— 超低停顿

ZGC特点:
  - 停顿时间不超过10ms(与堆大小无关)
  - 支持TB级别的堆
  - 使用染色指针和读屏障技术
  
适用场景:
  - 对延迟极度敏感的应用
  - 超大堆内存的应用
  - 实时性要求高的系统

收集器选择建议:

场景推荐收集器说明
小型应用(堆<2G)Serial简单高效
Web应用(堆2-8G)G1平衡吞吐和延迟
大堆低延迟(堆>8G)ZGC超低停顿
后台计算(高吞吐)Parallel最大化吞吐量

8.1.3 类加载机制 ​

类的生命周期 ​

加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载
         └──────── 连接阶段 ────────┘

双亲委派模型 ​

java
/**
 * 双亲委派模型
 * 
 * 类加载器层次:
 * 
 * Bootstrap ClassLoader(启动类加载器)
 *     │  加载 rt.jar(java.lang.*, java.util.* 等核心类)
 *     ▼
 * Extension ClassLoader(扩展类加载器)
 *     │  加载 ext 目录下的jar包
 *     ▼
 * Application ClassLoader(应用类加载器)
 *     │  加载classpath下的类(我们的代码)
 *     ▼
 * Custom ClassLoader(自定义类加载器)
 * 
 * 双亲委派流程:
 * 1. 收到类加载请求,先委派给父加载器
 * 2. 父加载器无法加载,才自己尝试加载
 * 3. 保证核心类不被篡改(安全机制)
 * 
 * 前端对比:
 * 类似于模块加载器的解析链
 * webpack resolve.modules 的查找顺序
 * 或者Node.js require的查找路径
 */
public class ClassLoaderDemo {
    
    public static void main(String[] args) {
        // 查看类加载器
        ClassLoader loader = ClassLoaderDemo.class.getClassLoader();
        System.out.println("当前类加载器: " + loader);
        // AppClassLoader
        
        System.out.println("父加载器: " + loader.getParent());
        // PlatformClassLoader (JDK 9+) / ExtClassLoader (JDK 8)
        
        System.out.println("祖父加载器: " + loader.getParent().getParent());
        // null (Bootstrap由C++实现,Java中返回null)
        
        // 核心类由Bootstrap加载
        System.out.println("String加载器: " + String.class.getClassLoader());
        // null(Bootstrap加载的类返回null)
    }
}

打破双亲委派 ​

java
/**
 * 自定义类加载器 —— 打破双亲委派
 * 
 * 应用场景:
 * 1. Tomcat:每个Web应用有独立的类加载器
 * 2. OSGi/热部署:运行时替换类
 * 3. SPI机制:JDBC驱动加载
 */
public class CustomClassLoader extends ClassLoader {
    
    private String classPath;
    
    public CustomClassLoader(String classPath) {
        this.classPath = classPath;
    }
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            // 读取.class文件的字节码
            String fileName = classPath + "/" + name.replace('.', '/') + ".class";
            java.io.FileInputStream fis = new java.io.FileInputStream(fileName);
            byte[] bytes = new byte[fis.available()];
            fis.read(bytes);
            fis.close();
            
            // 调用defineClass将字节码转为Class对象
            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            throw new ClassNotFoundException(name, e);
        }
    }
    
    // 使用示例
    public static void main(String[] args) throws Exception {
        CustomClassLoader myLoader = new CustomClassLoader("D:/classes");
        Class<?> clazz = myLoader.loadClass("com.example.MyService");
        Object instance = clazz.getDeclaredConstructor().newInstance();
        System.out.println("加载的类: " + instance.getClass().getName());
        System.out.println("加载器: " + instance.getClass().getClassLoader());
    }
}

8.1.4 JVM调优参数 ​

常用JVM参数 ​

bash
# ===== 堆内存参数 =====
-Xms512m                    # 初始堆大小(建议与-Xmx相同,避免动态扩缩)
-Xmx2g                      # 最大堆大小
-Xmn512m                    # 新生代大小(一般为堆的1/3到1/4)

# ===== 栈内存参数 =====
-Xss256k                    # 每个线程的栈大小(默认1MB)

# ===== 方法区参数 =====
-XX:MetaspaceSize=128m      # 元空间初始大小
-XX:MaxMetaspaceSize=256m   # 元空间最大大小

# ===== GC相关参数 =====
-XX:+UseG1GC                # 使用G1收集器
-XX:+UseZGC                 # 使用ZGC收集器
-XX:MaxGCPauseMillis=200    # 期望最大GC停顿时间(毫秒)
-XX:G1HeapRegionSize=4m     # G1 Region大小

# ===== GC日志参数 =====
-Xlog:gc*:file=gc.log       # GC日志输出到文件(JDK 9+)
-verbose:gc                 # 打印GC信息(JDK 8)
-XX:+PrintGCDetails         # 打印GC详细信息(JDK 8)
-XX:+PrintGCDateStamps      # 打印GC时间戳

# ===== OOM时自动dump =====
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heapdump.hprof

# ===== 完整的生产环境配置示例 =====
java -server \
  -Xms4g \
  -Xmx4g \
  -Xmn1g \
  -Xss512k \
  -XX:MetaspaceSize=256m \
  -XX:MaxMetaspaceSize=512m \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 \
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/data/logs/heapdump.hprof \
  -Xlog:gc*:file=/data/logs/gc.log:time,tags:filecount=5,filesize=50m \
  -jar app.jar

调优参数实战示例 ​

bash
# 场景1:小内存应用(Spring Boot微服务)
java -Xms512m -Xmx512m -Xmn128m \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=100 \
     -jar service.jar

# 场景2:大内存应用(电商系统)
java -Xms8g -Xmx8g -Xmn2g \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \
     -XX:G1HeapRegionSize=8m \
     -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=/data/dumps/heap.hprof \
     -jar app.jar

# 场景3:低延迟应用(实时系统)
java -Xms16g -Xmx16g \
     -XX:+UseZGC \
     -XX:+UnlockExperimentalVMOptions \
     -jar realtime-app.jar

# 场景4:高吞吐应用(批处理)
java -Xms4g -Xmx4g \
     -XX:+UseParallelGC \
     -XX:ParallelGCThreads=8 \
     -jar batch-app.jar

8.1.5 JVM排查工具 ​

jps —— 查看Java进程 ​

bash
# 列出所有Java进程
jps
# 输出示例:
# 12345 MyApp
# 67890 Jps

# 显示详细信息
jps -l
# 输出示例:
# 12345 com.example.MyApp
# 67890 sun.tools.jps.Jps

# 显示JVM参数
jps -v

jstat —— 监控GC状态 ​

bash
# 监控GC情况(每1秒输出一次,共100次)
jstat -gcutil <pid> 1000 100

# 输出示例:
#  S0     S1     E      O      M     CCS    YGC   YGCT    FGC   FGCT    GCT
#  0.00  45.23  67.89  23.45  95.67  89.12   120   2.345    3    0.456   2.801
#
# S0/S1 = Survivor区使用率
# E     = Eden区使用率
# O     = 老年代使用率
# M     = 元空间使用率
# YGC   = Young GC次数
# YGCT  = Young GC耗时(秒)
# FGC   = Full GC次数
# FGCT  = Full GC耗时(秒)
# GCT   = GC总耗时

# 监控类加载
jstat -class <pid> 1000

jstack —— 线程堆栈分析 ​

bash
# 导出线程堆栈
jstack <pid> > thread_dump.txt

# 典型使用场景:排查死锁
jstack <pid> | grep -A 20 "deadlock"

# 排查CPU占用高的线程
# 1. 找到Java进程PID
jps -l
# 2. 找到进程内CPU最高的线程TID
top -Hp <pid>
# 3. 将TID转为16进制
printf "%x\n" 12345
# 4. 在jstack输出中搜索
jstack <pid> | grep -A 30 "nid=0x3039"

jmap —— 堆内存分析 ​

bash
# 查看堆内存使用情况
jmap -heap <pid>

# 导出堆dump文件(用于离线分析)
jmap -dump:format=b,file=heap.hprof <pid>

# 查看对象统计信息
jmap -histo <pid> | head -20
# 输出示例:
#  num     #instances   #bytes   class name
#  1       500000       24000000 [B
#  2       200000       12800000 com.example.User
#  3       100000       6400000  java.util.HashMap$Node

Arthas —— 阿里开源的Java诊断神器 ​

bash
# 安装Arthas
# Windows:
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

# 常用命令

# 1. dashboard —— 实时查看系统面板
dashboard
# 显示线程、内存、GC、运行时信息

# 2. thread —— 查看线程信息
thread                    # 列出所有线程
thread -n 3               # 列出CPU占用最高的3个线程
thread <thread-id>        # 查看指定线程堆栈
thread -b                 # 找出阻塞其他线程的线程

# 3. jad —— 反编译类(热修复排查)
jad com.example.UserService
# 输出反编译后的源代码

# 4. watch —— 观察方法调用
watch com.example.UserService getUser '{params, returnObj, throwExp}' -x 3
# 观察getUser方法的入参、返回值和异常

# 5. trace —— 方法调用链路追踪
trace com.example.OrderService createOrder '#cost > 100'
# 追踪createOrder方法内部调用耗时,只显示耗时>100ms的

# 6. sc/sm —— 搜索类/方法
sc *UserService*          # 搜索类
sm com.example.UserService  # 查看类的方法信息

# 7. monitor —— 方法调用统计
monitor -c 5 com.example.UserService getUser
# 每5秒统计一次getUser的调用次数、成功率、平均耗时

# 8. heapdump —— 导出堆内存
heapdump /tmp/dump.hprof

前端对比:

JVM工具前端对应工具用途
jps浏览器进程管理器查看运行中的进程
jstatPerformance面板监控性能指标
jstackPerformance面板Threads线程分析
jmapMemory面板内存分析
ArthasChrome DevTools全家桶综合诊断
VisualVMLighthouse可视化分析
MAT(Memory Analyzer)Memory面板Heap Snapshot堆内存深度分析

8.2 性能优化(第3-4周) ​

8.2.1 SQL优化 ​

执行计划分析 ​

sql
-- 查看执行计划(MySQL)
EXPLAIN SELECT * FROM users WHERE username = 'test';

-- 输出字段说明:
-- id:          查询序号
-- select_type: 查询类型(SIMPLE/PRIMARY/SUBQUERY/UNION)
-- table:       访问的表
-- type:        连接类型(性能从好到差)
--              system > const > eq_ref > ref > range > index > ALL
-- possible_keys: 可能使用的索引
-- key:         实际使用的索引
-- key_len:     索引长度
-- rows:        预计扫描行数(越少越好)
-- Extra:       额外信息
--              Using index(覆盖索引)
--              Using where(需要过滤)
--              Using filesort(需要排序,需优化)
--              Using temporary(使用临时表,需优化)

执行计划示例分析:

sql
-- 慢查询示例
EXPLAIN SELECT u.username, o.order_no, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.age > 20 AND o.status = 1
ORDER BY o.amount DESC;

-- 分析结果:
-- users表:type=ALL(全表扫描!需要加索引)
-- orders表:type=ref(使用了索引)
-- Extra: Using filesort(需要额外排序,考虑加索引)

-- 优化方案:
-- 1. users表age字段加索引
ALTER TABLE users ADD INDEX idx_age (age);

-- 2. orders表联合索引
ALTER TABLE orders ADD INDEX idx_user_status_amount (user_id, status, amount);

-- 3. 优化后的查询
EXPLAIN SELECT u.username, o.order_no, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.age > 20 AND o.status = 1
ORDER BY o.amount DESC;

索引优化 ​

sql
-- ===== 索引类型 =====

-- 1. 普通索引
CREATE INDEX idx_username ON users(username);

-- 2. 唯一索引
CREATE UNIQUE INDEX uk_email ON users(email);

-- 3. 联合索引(最左前缀原则)
CREATE INDEX idx_name_age_status ON users(username, age, status);
-- 可以匹配的查询:
-- WHERE username = 'test'                     ✓
-- WHERE username = 'test' AND age = 25        ✓
-- WHERE username = 'test' AND age = 25 AND status = 1  ✓
-- WHERE age = 25                              ✗(缺少最左列)
-- WHERE age = 25 AND status = 1               ✗(缺少最左列)

-- 4. 覆盖索引(避免回表)
-- 如果索引包含了查询需要的所有字段,不需要回表查数据
CREATE INDEX idx_covering ON users(username, age, email);
-- 这个查询可以使用覆盖索引:
SELECT username, age, email FROM users WHERE username = 'test';
-- Extra显示:Using index

-- 5. 前缀索引(节省空间)
CREATE INDEX idx_email_prefix ON users(email(10));

-- ===== 索引失效场景 =====

-- 1. 对索引列使用函数
SELECT * FROM users WHERE UPPER(username) = 'TEST';  -- 索引失效
-- 优化:存储时就统一格式

-- 2. 隐式类型转换
SELECT * FROM users WHERE phone = 13800138000;  -- phone是VARCHAR,传入数字
-- 优化:传入字符串 WHERE phone = '13800138000'

-- 3. LIKE以%开头
SELECT * FROM users WHERE username LIKE '%test';  -- 索引失效
SELECT * FROM users WHERE username LIKE 'test%';  -- 索引有效

-- 4. OR条件(部分列没有索引)
SELECT * FROM users WHERE username = 'test' OR age = 25;  -- 如果age没有索引则全表扫描

-- 5. NOT IN / NOT EXISTS
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders);
-- 优化为 LEFT JOIN:
SELECT u.* FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.user_id IS NULL;

-- 6. 不等于
SELECT * FROM users WHERE status != 1;  -- 可能索引失效

-- ===== 索引设计原则 =====
-- 1. 选择性高的列优先(区分度高的列)
-- 2. 频繁查询的列加索引
-- 3. 联合索引注意列顺序(区分度高的在前)
-- 4. 避免过多索引(影响写入性能)
-- 5. 长字符串考虑前缀索引

SQL优化实战 ​

sql
-- ===== 优化1:避免SELECT * =====
-- 慢
SELECT * FROM users WHERE age > 20;
-- 快(减少数据传输量,可能使用覆盖索引)
SELECT id, username, email FROM users WHERE age > 20;

-- ===== 优化2:分页优化 =====
-- 深分页问题(越往后越慢)
SELECT * FROM users LIMIT 1000000, 10;  -- 扫描1000010行,丢弃1000000行

-- 优化方案1:延迟关联
SELECT u.* FROM users u
INNER JOIN (SELECT id FROM users ORDER BY id LIMIT 1000000, 10) tmp
ON u.id = tmp.id;

-- 优化方案2:游标分页(推荐)
-- 前端传入上一页最后一条的id
SELECT * FROM users WHERE id > #{lastId} ORDER BY id LIMIT 10;

-- ===== 优化3:COUNT优化 =====
-- 慢
SELECT COUNT(*) FROM users WHERE status = 1;
-- 如果只需要判断是否有数据
SELECT 1 FROM users WHERE status = 1 LIMIT 1;

-- ===== 优化4:JOIN优化 =====
-- 小表驱动大表
-- 确保JOIN字段有索引
-- 避免在JOIN条件中使用函数

-- ===== 优化5:子查询优化 =====
-- 慢(相关子查询)
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE amount > 100);
-- 快(改为JOIN)
SELECT DISTINCT u.* FROM users u INNER JOIN orders o ON u.id = o.user_id WHERE o.amount > 100;

-- ===== 优化6:批量操作 =====
-- 逐条插入(慢)
INSERT INTO users (username, email) VALUES ('a', 'a@test.com');
INSERT INTO users (username, email) VALUES ('b', 'b@test.com');
INSERT INTO users (username, email) VALUES ('c', 'c@test.com');

-- 批量插入(快)
INSERT INTO users (username, email) VALUES 
('a', 'a@test.com'),
('b', 'b@test.com'),
('c', 'c@test.com');

8.2.2 连接池调优 ​

HikariCP配置优化 ​

yaml
# application.yml - HikariCP配置(Spring Boot默认连接池)
spring:
  datasource:
    hikari:
      # ===== 核心参数 =====
      maximum-pool-size: 20          # 最大连接数(默认10)
      minimum-idle: 5                # 最小空闲连接数
      connection-timeout: 30000      # 获取连接超时时间(毫秒,默认30s)
      idle-timeout: 600000           # 空闲连接存活时间(毫秒,默认10min)
      max-lifetime: 1800000          # 连接最大存活时间(毫秒,默认30min)
      
      # ===== 高级参数 =====
      pool-name: MyHikariPool        # 连接池名称(方便日志排查)
      validation-timeout: 5000       # 连接验证超时
      leak-detection-threshold: 60000 # 连接泄漏检测阈值(0为禁用)
      
      # ===== 连接测试 =====
      connection-test-query: SELECT 1
      
      # ===== 数据源配置 =====
      jdbc-url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
      username: root
      password: yourpassword
      driver-class-name: com.mysql.cj.jdbc.Driver

连接池参数调优公式:

最大连接数 = ((核心数 * 2) + 有效磁盘数)
参考公式:connections = ((CPU核心数 * 2) + 磁盘数)

示例:
- 8核CPU,1块SSD:((8 * 2) + 1) = 17,取整20
- 4核CPU,1块HDD:((4 * 2) + 1) = 9,取整10

注意:
- 连接数不是越大越好(过多连接反而降低性能)
- 多个服务共用一个数据库时,总连接数不能超过数据库上限
- MySQL默认max_connections=151,需要根据实际情况调整

连接池监控 ​

java
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PoolMonitorController {
    
    @Autowired
    private HikariDataSource hikariDataSource;
    
    @GetMapping("/pool/status")
    public String getPoolStatus() {
        HikariPoolMXBean pool = hikariDataSource.getHikariPoolMXBean();
        
        return String.format(
            "活跃连接: %d, 空闲连接: %d, 总连接: %d, 等待线程: %d",
            pool.getActiveConnections(),
            pool.getIdleConnections(),
            pool.getTotalConnections(),
            pool.getThreadsAwaitingConnection()
        );
    }
}

8.2.3 缓存策略优化 ​

Spring Cache + Redis ​

java
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

@Service
@CacheConfig(cacheNames = "users")  // 默认缓存名称
public class UserService {
    
    // ===== @Cacheable:先查缓存,没有再查数据库并缓存 =====
    @Cacheable(key = "#id")
    public User findById(Long id) {
        return userMapper.selectById(id);
    }
    
    // 自定义key生成规则
    @Cacheable(key = "'user:' + #id")
    public User findByIdWithPrefix(Long id) {
        return userMapper.selectById(id);
    }
    
    // 条件缓存(只缓存age>18的)
    @Cacheable(key = "#id", condition = "#id > 0")
    public User findByIdConditional(Long id) {
        return userMapper.selectById(id);
    }
    
    // 指定缓存过期时间(需要自定义CacheManager)
    @Cacheable(key = "#id", unless = "#result == null")
    public User findByIdNotNull(Long id) {
        return userMapper.selectById(id);
    }
    
    // ===== @CachePut:更新缓存 =====
    @CachePut(key = "#user.id")
    public User updateUser(User user) {
        userMapper.updateById(user);
        return user;  // 返回值会放入缓存
    }
    
    // ===== @CacheEvict:删除缓存 =====
    @CacheEvict(key = "#id")
    public void deleteUser(Long id) {
        userMapper.deleteById(id);
    }
    
    // 删除整个缓存
    @CacheEvict(allEntries = true)
    public void clearAllCache() {
        // 清空users缓存
    }
    
    // ===== @Caching:组合多个缓存操作 =====
    @Caching(evict = {
        @CacheEvict(key = "#user.id"),
        @CacheEvict(key = "'name:' + #user.username")
    })
    public void updateUserEvictMultiple(User user) {
        userMapper.updateById(user);
    }
}

Redis缓存配置 ​

java
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableCaching  // 开启缓存
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        // 默认缓存配置
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))  // 默认过期时间30分钟
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()))
            .disableCachingNullValues();  // 不缓存null值
        
        // 针对不同缓存名设置不同的过期时间
        return RedisCacheManager.builder(factory)
            .cacheDefaults(defaultConfig)
            .withCacheConfiguration("users", 
                defaultConfig.entryTtl(Duration.ofHours(1)))
            .withCacheConfiguration("orders",
                defaultConfig.entryTtl(Duration.ofMinutes(10)))
            .withCacheConfiguration("products",
                defaultConfig.entryTtl(Duration.ofHours(2)))
            .build();
    }
}

缓存常见问题与解决方案 ​

java
/**
 * 缓存三大问题及解决方案
 */

// ===== 1. 缓存穿透:查询不存在的数据 =====
// 问题:每次都穿透缓存到数据库
// 解决:缓存空值
@Service
public class UserServiceWithPenetrationFix {
    
    @Cacheable(key = "#id", unless = "#result != null")
    public User findById(Long id) {
        User user = userMapper.selectById(id);
        if (user == null) {
            // 缓存空值,设置较短过期时间
            // 防止缓存穿透
            return null;  // 配合unless不缓存null,需要手动处理
        }
        return user;
    }
    
    // 使用布隆过滤器(Bloom Filter)预防
    // RedisBloom模块可以在缓存层判断key是否可能存在
}

// ===== 2. 缓存击穿:热点key过期,大量请求打到数据库 =====
// 解决:互斥锁(分布式锁)
@Service
public class UserServiceWithBreakdownFix {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    public User findByIdWithLock(Long id) {
        String key = "user:" + id;
        
        // 先查缓存
        String cached = redisTemplate.opsForValue().get(key);
        if (cached != null) {
            return JSON.parseObject(cached, User.class);
        }
        
        // 获取分布式锁
        String lockKey = "lock:user:" + id;
        Boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", Duration.ofSeconds(10));
        
        if (Boolean.TRUE.equals(locked)) {
            try {
                // 双重检查
                cached = redisTemplate.opsForValue().get(key);
                if (cached != null) {
                    return JSON.parseObject(cached, User.class);
                }
                
                // 查数据库
                User user = userMapper.selectById(id);
                redisTemplate.opsForValue().set(key, JSON.toJSONString(user), Duration.ofHours(1));
                return user;
            } finally {
                redisTemplate.delete(lockKey);
            }
        } else {
            // 获取锁失败,短暂休眠后重试
            try { Thread.sleep(50); } catch (InterruptedException e) {}
            return findByIdWithLock(id);
        }
    }
}

// ===== 3. 缓存雪崩:大量缓存同时过期 =====
// 解决:过期时间加随机值
@Service
public class UserServiceWithAvalancheFix {
    
    private final Random random = new Random();
    
    public void cacheUser(User user) {
        String key = "user:" + user.getId();
        // 基础时间 + 随机时间(避免同时过期)
        int baseTtl = 3600;  // 1小时
        int randomTtl = random.nextInt(600);  // 0-10分钟随机
        redisTemplate.opsForValue().set(key, 
            JSON.toJSONString(user), 
            Duration.ofSeconds(baseTtl + randomTtl));
    }
}

8.2.4 异步编程与响应式(Spring WebFlux) ​

Spring WebFlux入门 ​

java
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;

/**
 * WebFlux Controller
 * 
 * 前端对比:
 * Spring MVC(Servlet模型)→ 类似Express/Koa(同步阻塞)
 * WebFlux(响应式模型)→ 类似Node.js事件循环(非阻塞)
 * 
 * Mono  → Promise/Observable(0或1个元素)
 * Flux  → Observable(0到N个元素)
 */
@RestController
@RequestMapping("/api/users")
public class UserReactiveController {
    
    @Autowired
    private UserReactiveService userService;
    
    // ===== 基本用法 =====
    
    // Mono:返回单个对象
    @GetMapping("/{id}")
    public Mono<User> getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
    
    // Flux:返回多个对象(流式返回)
    @GetMapping("")
    public Flux<User> getAllUsers() {
        return userService.findAll();
    }
    
    // ===== 服务端推送(SSE)=====
    @GetMapping(value = "/stream", produces = "text/event-stream")
    public Flux<String> stream() {
        return Flux.interval(Duration.ofSeconds(1))
                   .map(seq -> "data: 消息 " + seq + "\n\n");
    }
    
    // ===== 创建Mono =====
    public Mono<String> createMonoExamples() {
        // 空值
        Mono<String> empty = Mono.empty();
        
        // 固定值
        Mono<String> just = Mono.just("Hello");
        
        // 异步操作
        Mono<String> fromCallable = Mono.fromCallable(() -> {
            Thread.sleep(1000);  // 模拟耗时操作
            return "Result";
        });
        
        // 从Future转换
        Mono<String> fromFuture = Mono.fromFuture(() -> CompletableFuture.supplyAsync(() -> "Async"));
        
        return just;
    }
    
    // ===== 操作符 =====
    public Mono<String> operatorExamples() {
        return Mono.just("Hello")
            .map(s -> s + " World")           // 转换
            .filter(s -> s.length() > 5)       // 过滤
            .flatMap(s -> Mono.just(s.toUpperCase()))  // 异步转换
            .doOnNext(s -> System.out.println("结果: " + s))  // 副作用
            .onErrorResume(ex -> Mono.just("Error"))  // 错误处理
            .timeout(Duration.ofSeconds(5));   // 超时
    }
}

响应式Service ​

java
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

@Service
public class UserReactiveService {
    
    // 模拟异步数据库操作
    public Mono<User> findById(Long id) {
        return Mono.fromCallable(() -> {
            // 模拟数据库查询
            Thread.sleep(100);
            return new User(id, "user" + id);
        }).subscribeOn(Schedulers.boundedElastic());  // 在弹性线程池执行
    }
    
    public Flux<User> findAll() {
        return Flux.range(1, 100)
                   .map(id -> new User((long) id, "user" + id))
                   .delayElements(Duration.ofMillis(10));  // 模拟流式返回
    }
    
    // 组合多个异步操作
    public Mono<UserProfile> getUserProfile(Long userId) {
        Mono<User> userMono = findById(userId);
        Mono<List<Order>> ordersMono = findOrdersByUserId(userId).collectList();
        Mono<Integer> countMono = countPosts(userId);
        
        // 并行执行,组合结果
        return Mono.zip(userMono, ordersMono, countMono)
                   .map(tuple -> {
                       User user = tuple.getT1();
                       List<Order> orders = tuple.getT2();
                       Integer postCount = tuple.getT3();
                       return new UserProfile(user, orders, postCount);
                   });
    }
    
    public Flux<Order> findOrdersByUserId(Long userId) {
        return Flux.just(
            new Order(1L, "ORDER001", 99.9),
            new Order(2L, "ORDER002", 199.9)
        );
    }
    
    public Mono<Integer> countPosts(Long userId) {
        return Mono.just(42);
    }
}

WebFlux配置 ​

yaml
# application.yml
spring:
  webflux:
    base-path: /api
  
# Netty服务器配置
server:
  port: 8080
  netty:
    connection-timeout: 20s
xml
<!-- pom.xml - WebFlux依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<!-- 响应式Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

<!-- 响应式MongoDB -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>

8.2.5 高并发场景处理 ​

限流 ​

java
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.stereotype.Component;

/**
 * 限流算法实现
 * 
 * 前端对比:
 * 类似于请求队列、防抖节流的概念
 * 但限流是在服务端控制请求速率
 */

// ===== 1. 固定窗口限流 =====
@Component
public class FixedWindowRateLimiter {
    
    // key -> [计数器, 窗口开始时间]
    private final ConcurrentHashMap<String, long[]> counters = new ConcurrentHashMap<>();
    
    public boolean tryAcquire(String key, int limit, long windowMs) {
        long now = System.currentTimeMillis();
        
        counters.putIfAbsent(key, new long[]{0, now});
        long[] counter = counters.get(key);
        
        // 窗口过期,重置
        if (now - counter[1] > windowMs) {
            counter[0] = 0;
            counter[1] = now;
        }
        
        // 判断是否超限
        if (counter[0] < limit) {
            counter[0]++;
            return true;
        }
        return false;
    }
}

// ===== 2. 滑动窗口限流 =====
@Component
public class SlidingWindowRateLimiter {
    
    private final ConcurrentHashMap<String, LinkedList<Long>> requestTimes = new ConcurrentHashMap<>();
    
    public boolean tryAcquire(String key, int limit, long windowMs) {
        long now = System.currentTimeMillis();
        
        requestTimes.putIfAbsent(key, new LinkedList<>());
        LinkedList<Long> times = requestTimes.get(key);
        
        synchronized (times) {
            // 移除窗口外的记录
            while (!times.isEmpty() && now - times.getFirst() > windowMs) {
                times.removeFirst();
            }
            
            if (times.size() < limit) {
                times.addLast(now);
                return true;
            }
            return false;
        }
    }
}

// ===== 3. 令牌桶限流(使用Guava RateLimiter)=====
import com.google.common.util.concurrent.RateLimiter;

@Component
public class TokenBucketLimiter {
    
    // 每秒产生10个令牌
    private final RateLimiter rateLimiter = RateLimiter.create(10.0);
    
    public boolean tryAcquire() {
        return rateLimiter.tryAcquire();  // 非阻塞
        // rateLimiter.acquire();  // 阻塞等待
    }
}

使用注解实现限流 ​

java
// 自定义限流注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    int limit() default 100;         // 限流数量
    long window() default 60;        // 时间窗口(秒)
    String key() default "";         // 限流key(默认使用方法名)
    RateLimitType type() default RateLimitType.IP;  // 限流维度
}

public enum RateLimitType {
    IP,      // 按IP限流
    USER,    // 按用户限流
    GLOBAL   // 全局限流
}

// AOP实现
@Aspect
@Component
public class RateLimitAspect {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
        String key = "rate_limit:" + rateLimit.key();
        
        // 根据限流维度设置key
        switch (rateLimit.type()) {
            case IP:
                key += ":" + getClientIp();
                break;
            case USER:
                key += ":" + getCurrentUserId();
                break;
        }
        
        Long count = redisTemplate.opsForValue().increment(key);
        if (count == 1) {
            redisTemplate.expire(key, Duration.ofSeconds(rateLimit.window()));
        }
        
        if (count > rateLimit.limit()) {
            throw new RuntimeException("请求过于频繁,请稍后再试");
        }
        
        return point.proceed();
    }
}

// 使用
@RestController
public class OrderController {
    
    @RateLimit(limit = 10, window = 60, type = RateLimitType.USER)
    @PostMapping("/create")
    public Result createOrder(@RequestBody OrderRequest request) {
        // 每个用户60秒最多10次下单请求
        return orderService.createOrder(request);
    }
}

异步任务处理 ​

java
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import java.util.concurrent.Future;

@Service
@EnableAsync  // 开启异步支持
public class AsyncService {
    
    // ===== 基本异步方法 =====
    @Async
    public void sendEmail(String to, String content) {
        // 异步发送邮件,不阻塞主线程
        System.out.println("发送邮件线程: " + Thread.currentThread().getName());
        // 发送邮件逻辑...
    }
    
    // ===== 有返回值的异步方法 =====
    @Async("taskExecutor")  // 指定线程池
    public Future<String> processFile(String filePath) {
        // 异步处理文件
        String result = doProcess(filePath);
        return new AsyncResult<>(result);
    }
    
    // ===== 异步编排(CompletableFuture)=====
    @Async("taskExecutor")
    public CompletableFuture<UserInfo> getUserInfo(Long userId) {
        User user = userMapper.selectById(userId);
        return CompletableFuture.completedFuture(toUserInfo(user));
    }
    
    @Async("taskExecutor")
    public CompletableFuture<List<Order>> getUserOrders(Long userId) {
        return CompletableFuture.completedFuture(orderMapper.selectByUserId(userId));
    }
    
    @Async("taskExecutor")
    public CompletableFuture<Integer> getUserPoints(Long userId) {
        return CompletableFuture.completedFuture(100);
    }
    
    // 并行获取用户信息
    public UserProfile getUserProfile(Long userId) {
        CompletableFuture<UserInfo> infoFuture = getUserInfo(userId);
        CompletableFuture<List<Order>> ordersFuture = getUserOrders(userId);
        CompletableFuture<Integer> pointsFuture = getUserPoints(userId);
        
        // 等待所有完成
        CompletableFuture.allOf(infoFuture, ordersFuture, pointsFuture).join();
        
        return new UserProfile(
            infoFuture.join(),
            ordersFuture.join(),
            pointsFuture.join()
        );
    }
}

自定义异步线程池 ​

java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class AsyncConfig {
    
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        // 核心参数
        executor.setCorePoolSize(5);           // 核心线程数
        executor.setMaxPoolSize(20);           // 最大线程数
        executor.setQueueCapacity(200);        // 队列容量
        executor.setKeepAliveSeconds(60);      // 空闲线程存活时间
        
        // 线程名前缀(方便排查问题)
        executor.setThreadNamePrefix("async-task-");
        
        // 拒绝策略:由调用线程执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        
        // 应用关闭时等待任务完成
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        
        executor.initialize();
        return executor;
    }
}

8.3 架构设计(第5-6周) ​

8.3.1 设计模式 ​

创建型模式 ​

1. 单例模式(Singleton)

java
/**
 * 单例模式:确保一个类只有一个实例
 * 
 * 前端对比:Vue的全局store、Redux的store都是单例思想
 * 在JavaScript中通过闭包或模块系统实现
 */

// 方式1:饿汉式(线程安全,类加载时创建)
public class Singleton1 {
    private static final Singleton1 INSTANCE = new Singleton1();
    
    private Singleton1() {}  // 私有构造方法
    
    public static Singleton1 getInstance() {
        return INSTANCE;
    }
}

// 方式2:双重检查锁(推荐,懒加载+线程安全)
public class Singleton2 {
    private static volatile Singleton2 instance;  // volatile防止指令重排
    
    private Singleton2() {}
    
    public static Singleton2 getInstance() {
        if (instance == null) {                    // 第一次检查(无锁)
            synchronized (Singleton2.class) {      // 加锁
                if (instance == null) {             // 第二次检查(有锁)
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }
}

// 方式3:静态内部类(推荐,优雅且线程安全)
public class Singleton3 {
    private Singleton3() {}
    
    // 静态内部类,只有调用getInstance时才加载
    private static class Holder {
        private static final Singleton3 INSTANCE = new Singleton3();
    }
    
    public static Singleton3 getInstance() {
        return Holder.INSTANCE;
    }
}

// 方式4:枚举(最简洁,天然防反射和反序列化破坏)
public enum Singleton4 {
    INSTANCE;
    
    public void doSomething() {
        System.out.println("doing something...");
    }
}

// Spring中的单例(默认scope=singleton)
@Component
public class SpringSingleton {
    // Spring容器保证单例
    // 注意:不要持有可变状态!
}

2. 工厂模式(Factory)

java
/**
 * 工厂模式:将对象创建与使用分离
 * 
 * 前端对比:
 * React.createElement() 就是一种工厂方法
 * Vue的createApp() 也是工厂模式
 */

// ===== 简单工厂 =====
public interface Payment {
    void pay(double amount);
}

public class AliPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("支付宝支付: " + amount);
    }
}

public class WechatPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("微信支付: " + amount);
    }
}

public class UnionPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("银联支付: " + amount);
    }
}

// 简单工厂
public class PaymentFactory {
    public static Payment create(String type) {
        switch (type) {
            case "ali": return new AliPay();
            case "wechat": return new WechatPay();
            case "union": return new UnionPay();
            default: throw new IllegalArgumentException("不支持的支付方式: " + type);
        }
    }
}

// 使用
Payment payment = PaymentFactory.create("ali");
payment.pay(100.0);

// ===== 工厂方法(Spring大量使用)=====
public interface PaymentFactory2 {
    Payment create();
}

public class AliPayFactory implements PaymentFactory2 {
    @Override
    public Payment create() {
        return new AliPay();
    }
}

public class WechatPayFactory implements PaymentFactory2 {
    @Override
    public Payment create() {
        return new WechatPay();
    }
}

// ===== 抽象工厂 =====
public interface UIFactory {
    Button createButton();
    Input createInput();
    Dialog createDialog();
}

// 不同主题的实现
public class LightThemeFactory implements UIFactory {
    @Override
    public Button createButton() { return new LightButton(); }
    @Override
    public Input createInput() { return new LightInput(); }
    @Override
    public Dialog createDialog() { return new LightDialog(); }
}

public class DarkThemeFactory implements UIFactory {
    @Override
    public Button createButton() { return new DarkButton(); }
    @Override
    public Input createInput() { return new DarkInput(); }
    @Override
    public Dialog createDialog() { return new DarkDialog(); }
}

3. 建造者模式(Builder)

java
/**
 * 建造者模式:分步构建复杂对象
 * 
 * 前端对比:
 * 类似于链式调用配置对象
 * 或者Vue Router的router配置
 */

// Lombok的@Builder注解(最常用)
@Builder
@Data
public class HttpRequest {
    private String url;
    private String method;
    private Map<String, String> headers;
    private String body;
    private int timeout;
    private boolean retry;
}

// 使用
HttpRequest request = HttpRequest.builder()
    .url("https://api.example.com/users")
    .method("POST")
    .headers(Map.of("Content-Type", "application/json"))
    .body("{\"name\": \"test\"}")
    .timeout(5000)
    .retry(true)
    .build();

// 手动实现Builder模式
public class QueryBuilder {
    private String table;
    private List<String> columns = new ArrayList<>();
    private List<String> conditions = new ArrayList<>();
    private String orderBy;
    private int limit;
    
    public QueryBuilder from(String table) {
        this.table = table;
        return this;
    }
    
    public QueryBuilder select(String... columns) {
        this.columns.addAll(Arrays.asList(columns));
        return this;
    }
    
    public QueryBuilder where(String condition) {
        this.conditions.add(condition);
        return this;
    }
    
    public QueryBuilder orderBy(String orderBy) {
        this.orderBy = orderBy;
        return this;
    }
    
    public QueryBuilder limit(int limit) {
        this.limit = limit;
        return this;
    }
    
    public String build() {
        StringBuilder sql = new StringBuilder("SELECT ");
        sql.append(columns.isEmpty() ? "*" : String.join(", ", columns));
        sql.append(" FROM ").append(table);
        
        if (!conditions.isEmpty()) {
            sql.append(" WHERE ").append(String.join(" AND ", conditions));
        }
        
        if (orderBy != null) {
            sql.append(" ORDER BY ").append(orderBy);
        }
        
        if (limit > 0) {
            sql.append(" LIMIT ").append(limit);
        }
        
        return sql.toString();
    }
}

// 使用(链式调用,类似Stream API)
String sql = new QueryBuilder()
    .select("id", "username", "email")
    .from("users")
    .where("age > 18")
    .where("status = 1")
    .orderBy("created_at DESC")
    .limit(10)
    .build();
// SELECT id, username, email FROM users WHERE age > 18 AND status = 1 ORDER BY created_at DESC LIMIT 10

结构型模式 ​

4. 代理模式(Proxy)

java
/**
 * 代理模式:为对象提供替身以控制访问
 * 
 * 前端对比:
 * Vue 3的Proxy响应式就是代理模式
 * JavaScript的Proxy对象
 * 前端拦截器(Axios interceptor)
 */

// ===== JDK动态代理(Spring AOP核心)=====
import java.lang.reflect.*;

// 接口
public interface UserService {
    void save(User user);
    User findById(Long id);
}

// 实现类
public class UserServiceImpl implements UserService {
    @Override
    public void save(User user) {
        System.out.println("保存用户: " + user.getName());
    }
    
    @Override
    public User findById(Long id) {
        return new User(id, "test");
    }
}

// 代理处理器
public class LogProxyHandler implements InvocationHandler {
    
    private final Object target;  // 被代理对象
    
    public LogProxyHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("[LOG] 开始执行: " + method.getName());
        
        try {
            Object result = method.invoke(target, args);
            
            long elapsed = System.currentTimeMillis() - start;
            System.out.println("[LOG] 执行完成: " + method.getName() + ", 耗时: " + elapsed + "ms");
            
            return result;
        } catch (Exception e) {
            System.out.println("[LOG] 执行异常: " + method.getName() + ", 异常: " + e.getMessage());
            throw e;
        }
    }
}

// 使用
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    userService.getClass().getClassLoader(),
    userService.getClass().getInterfaces(),
    new LogProxyHandler(userService)
);

proxy.save(new User(1L, "张三"));
// 输出:
// [LOG] 开始执行: save
// 保存用户: 张三
// [LOG] 执行完成: save, 耗时: 0ms

5. 装饰器模式(Decorator)

java
/**
 * 装饰器模式:动态添加功能
 * 
 * 前端对比:
 * 高阶组件(HOC)
 * 中间件链(Koa中间件、Express中间件)
 */

// 组件接口
public interface DataSource {
    String getData();
}

// 基础组件
public class FileDataSource implements DataSource {
    private String filename;
    
    public FileDataSource(String filename) {
        this.filename = filename;
    }
    
    @Override
    public String getData() {
        return "文件内容";
    }
}

// 装饰器基类
public abstract class DataSourceDecorator implements DataSource {
    protected DataSource wrappee;
    
    public DataSourceDecorator(DataSource source) {
        this.wrappee = source;
    }
    
    @Override
    public String getData() {
        return wrappee.getData();
    }
}

// 加密装饰器
public class EncryptionDecorator extends DataSourceDecorator {
    @Override
    public String getData() {
        return encrypt(super.getData());
    }
    
    private String encrypt(String data) {
        return "加密(" + data + ")";
    }
}

// 压缩装饰器
public class CompressionDecorator extends DataSourceDecorator {
    @Override
    public String getData() {
        return compress(super.getData());
    }
    
    private String compress(String data) {
        return "压缩(" + data + ")";
    }
}

// 使用(类似Koa中间件的洋葱模型)
DataSource source = new CompressionDecorator(
    new EncryptionDecorator(
        new FileDataSource("data.txt")
    )
);
String result = source.getData();
// 结果:压缩(加密(文件内容))

行为型模式 ​

6. 策略模式(Strategy)

java
/**
 * 策略模式:定义一系列算法,使它们可以互相替换
 * 
 * 前端对比:
 * 表单验证策略
 * 排序算法选择
 * 不同环境的配置策略
 */

// 策略接口
public interface DiscountStrategy {
    double calculate(double price);
}

// 具体策略
public class NoDiscount implements DiscountStrategy {
    @Override
    public double calculate(double price) {
        return price;
    }
}

public class PercentDiscount implements DiscountStrategy {
    private double percent;
    
    public PercentDiscount(double percent) {
        this.percent = percent;
    }
    
    @Override
    public double calculate(double price) {
        return price * (1 - percent / 100);
    }
}

public class FullReduction implements DiscountStrategy {
    private double threshold;
    private double reduction;
    
    public FullReduction(double threshold, double reduction) {
        this.threshold = threshold;
        this.reduction = reduction;
    }
    
    @Override
    public double calculate(double price) {
        return price >= threshold ? price - reduction : price;
    }
}

// 上下文
public class PriceCalculator {
    private DiscountStrategy strategy;
    
    public void setStrategy(DiscountStrategy strategy) {
        this.strategy = strategy;
    }
    
    public double calculatePrice(double price) {
        return strategy.calculate(price);
    }
}

// 使用
PriceCalculator calculator = new PriceCalculator();

// 无折扣
calculator.setStrategy(new NoDiscount());
System.out.println(calculator.calculatePrice(200));  // 200

// 8折
calculator.setStrategy(new PercentDiscount(20));
System.out.println(calculator.calculatePrice(200));  // 160

// 满200减50
calculator.setStrategy(new FullReduction(200, 50));
System.out.println(calculator.calculatePrice(200));  // 150

// Spring中的策略模式(结合Map自动注册)
@Component
public class DiscountStrategyFactory {
    
    private final Map<String, DiscountStrategy> strategies = new ConcurrentHashMap<>();
    
    // Spring自动注入所有DiscountStrategy实现
    @Autowired
    public DiscountStrategyFactory(List<DiscountStrategy> strategyList) {
        for (DiscountStrategy strategy : strategyList) {
            strategies.put(strategy.getClass().getSimpleName(), strategy);
        }
    }
    
    public DiscountStrategy getStrategy(String name) {
        return strategies.getOrDefault(name, new NoDiscount());
    }
}

7. 观察者模式(Observer)

java
/**
 * 观察者模式:一对多的依赖关系,当一个对象改变时通知所有依赖者
 * 
 * 前端对比:
 * 事件监听(addEventListener)
 * Vue的事件系统($emit / $on)
 * Redux的subscribe
 * EventEmitter
 * RxJS的Observable
 */

// ===== Spring事件机制 =====

// 1. 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order;
    
    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
    
    public Order getOrder() {
        return order;
    }
}

// 2. 发布事件
@Service
public class OrderService {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    public void createOrder(Order order) {
        // 保存订单
        orderMapper.insert(order);
        
        // 发布事件(解耦后续操作)
        eventPublisher.publishEvent(new OrderCreatedEvent(this, order));
    }
}

// 3. 监听事件(多个监听器独立执行)
@Component
public class OrderEventListener {
    
    // 发送通知
    @EventListener
    @Async
    public void onOrderCreated(OrderCreatedEvent event) {
        Order order = event.getOrder();
        System.out.println("发送订单通知: " + order.getOrderNo());
    }
    
    // 扣减库存
    @EventListener
    @Async
    public void onOrderCreatedDeductStock(OrderCreatedEvent event) {
        Order order = event.getOrder();
        System.out.println("扣减库存: " + order.getProductId());
    }
    
    // 增加积分
    @EventListener(condition = "#event.order.amount > 100")
    public void onOrderCreatedAddPoints(OrderCreatedEvent event) {
        System.out.println("增加积分");
    }
}

// ===== 条件监听 =====
// 只处理特定类型的事件
@EventListener(classes = {OrderCreatedEvent.class})
public void handle(OrderCreatedEvent event) {
    // ...
}

8. 模板方法模式(Template Method)

java
/**
 * 模板方法模式:定义算法式骨架,将具体步骤延迟到子类
 * 
 * 前端对比:
 * React的组件生命周期
 * Vue的钩子函数(created, mounted等)
 */

// 抽象模板
public abstract class AbstractExportService<T> {
    
    // 模板方法(固定流程)
    public final void export(List<T> data, String filename) {
        // 1. 验证数据
        validate(data);
        
        // 2. 转换数据
        List<Map<String, Object>> rows = convert(data);
        
        // 3. 生成文件(子类实现)
        byte[] fileBytes = generateFile(rows);
        
        // 4. 上传文件
        String url = upload(fileBytes, filename);
        
        // 5. 记录日志
        log(filename, url, data.size());
    }
    
    private void validate(List<T> data) {
        if (data == null || data.isEmpty()) {
            throw new IllegalArgumentException("导出数据不能为空");
        }
    }
    
    protected abstract List<Map<String, Object>> convert(List<T> data);
    
    protected abstract byte[] generateFile(List<Map<String, Object>> rows);
    
    private String upload(byte[] bytes, String filename) {
        // 上传逻辑
        return "https://example.com/files/" + filename;
    }
    
    private void log(String filename, String url, int count) {
        System.out.printf("导出完成: %s, 共%d条, 地址: %s%n", filename, count, url);
    }
}

// Excel导出
public class ExcelExportService extends AbstractExportService<User> {
    
    @Override
    protected List<Map<String, Object>> convert(List<User> data) {
        return data.stream().map(user -> {
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("用户名", user.getName());
            row.put("邮箱", user.getEmail());
            row.put("年龄", user.getAge());
            return row;
        }).collect(Collectors.toList());
    }
    
    @Override
    protected byte[] generateFile(List<Map<String, Object>> rows) {
        // 使用POI生成Excel
        return new byte[0];  // 简化
    }
}

// CSV导出
public class CsvExportService extends AbstractExportService<User> {
    
    @Override
    protected List<Map<String, Object>> convert(List<User> data) {
        return data.stream().map(user -> {
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("name", user.getName());
            row.put("email", user.getEmail());
            row.put("age", user.getAge());
            return row;
        }).collect(Collectors.toList());
    }
    
    @Override
    protected byte[] generateFile(List<Map<String, Object>> rows) {
        // 生成CSV
        return new byte[0];  // 简化
    }
}

常用设计模式总结 ​

模式用途Spring中的使用
单例Bean默认scope所有Spring Bean
工厂创建对象BeanFactory, FactoryBean
代理AOPJDK动态代理, CGLIB
模板方法固定流程JdbcTemplate, RestTemplate
观察者事件驱动ApplicationEvent
策略算法切换Resource接口
适配器接口转换HandlerAdapter
装饰器增强功能BeanWrapper

8.3.2 DDD领域驱动设计 ​

DDD核心概念 ​

DDD分层架构:

┌─────────────────────────────────────────┐
│           用户接口层(Interface)          │
│    Controller, DTO, VO, 参数校验         │
├─────────────────────────────────────────┤
│           应用层(Application)           │
│    Service, 事务管理, 事件发布            │
├─────────────────────────────────────────┤
│           领域层(Domain)               │
│    Entity, ValueObject, Aggregate        │
│    DomainService, Repository接口         │
├─────────────────────────────────────────┤
│           基础设施层(Infrastructure)     │
│    Repository实现, 数据库, 消息队列       │
│    外部服务调用, 配置                     │
└─────────────────────────────────────────┘

前端对比:
- 前端分层:UI层 → 业务逻辑层 → 数据层 → API层
- DDD分层思想类似,但更加严谨
- 领域层相当于前端的业务逻辑核心

DDD代码示例 ​

java
// ===== 1. 实体(Entity)—— 有唯一标识 =====
public class Order {
    private OrderId orderId;           // 值对象作为ID
    private UserId userId;             // 值对象
    private OrderStatus status;        // 枚举
    private Money totalAmount;         // 值对象
    private List<OrderItem> items;     // 实体集合
    private LocalDateTime createdAt;
    
    // 实体行为(充血模型,而非贫血模型)
    public void pay() {
        if (this.status != OrderStatus.PENDING) {
            throw new BusinessException("订单状态不允许支付");
        }
        this.status = OrderStatus.PAID;
        // 领域事件
        registerEvent(new OrderPaidEvent(this.orderId));
    }
    
    public void cancel() {
        if (this.status == OrderStatus.SHIPPED) {
            throw new BusinessException("已发货订单不能取消");
        }
        this.status = OrderStatus.CANCELLED;
    }
    
    public void addItem(Product product, int quantity) {
        if (this.status != OrderStatus.PENDING) {
            throw new BusinessException("只能修改待支付订单");
        }
        OrderItem item = new OrderItem(product.getProductId(), product.getName(), 
                                        product.getPrice(), quantity);
        this.items.add(item);
        recalculateAmount();
    }
    
    private void recalculateAmount() {
        this.totalAmount = items.stream()
            .map(OrderItem::getSubtotal)
            .reduce(Money.ZERO, Money::add);
    }
}

// ===== 2. 值对象(Value Object)—— 无唯一标识,不可变 =====
public class Money {
    public static final Money ZERO = new Money(BigDecimal.ZERO);
    
    private final BigDecimal amount;
    private final Currency currency;
    
    public Money(BigDecimal amount, Currency currency) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("金额不能为负数");
        }
        this.amount = amount;
        this.currency = currency;
    }
    
    public Money(BigDecimal amount) {
        this(amount, Currency.CNY);
    }
    
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("不同币种不能相加");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }
    
    public Money multiply(int quantity) {
        return new Money(this.amount.multiply(BigDecimal.valueOf(quantity)), this.currency);
    }
    
    // equals和hashCode基于值(而非引用)
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Money money = (Money) o;
        return amount.compareTo(money.amount) == 0 && currency.equals(money.currency);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(amount.stripTrailingZeros(), currency);
    }
}

// ===== 3. 聚合根(Aggregate Root)=====
public class OrderAggregate {
    private Order order;
    
    // 聚合根保证一致性
    public void submitOrder(CreateOrderCommand command) {
        // 1. 创建订单
        Order order = Order.create(command.getUserId(), command.getItems());
        
        // 2. 校验库存(调用领域服务)
        inventoryService.checkStock(command.getItems());
        
        // 3. 计算优惠(调用领域服务)
        Money discount = promotionService.calculateDiscount(order.getTotalAmount());
        order.applyDiscount(discount);
        
        // 4. 发布领域事件
        order.registerEvent(new OrderCreatedEvent(order.getOrderId()));
        
        this.order = order;
    }
}

// ===== 4. 领域服务(Domain Service)=====
@Service
public class OrderDomainService {
    
    // 跨聚合的业务逻辑放在领域服务
    public void transferOrder(Order source, Order target, User operator) {
        // 涉及多个聚合的操作
        source.transfer();
        target.receive();
        // 发布事件
    }
}

// ===== 5. 仓储接口(Repository)=====
public interface OrderRepository {
    Order findById(OrderId orderId);
    void save(Order order);
    void remove(OrderId orderId);
    List<Order> findByUserId(UserId userId);
}

// ===== 6. 应用服务(Application Service)=====
@Service
public class OrderApplicationService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private EventPublisher eventPublisher;
    
    @Transactional
    public OrderDTO createOrder(CreateOrderCommand command) {
        // 1. 创建聚合根
        OrderAggregate aggregate = new OrderAggregate();
        aggregate.submitOrder(command);
        
        // 2. 保存
        Order order = aggregate.getOrder();
        orderRepository.save(order);
        
        // 3. 发布事件
        eventPublisher.publish(order.getEvents());
        
        // 4. 返回DTO
        return OrderConverter.toDTO(order);
    }
}

8.3.3 六边形架构 / 整洁架构 ​

java
/**
 * 六边形架构(端口与适配器架构)
 * 
 * 核心思想:业务逻辑不依赖任何外部技术
 * 
 *        ┌──────────────────────────┐
 *        │     外部适配器(驱动侧)    │
 *        │  REST API / gRPC / MQ    │
 *        ├──────────────────────────┤
 *        │      应用层(端口)         │
 *        │  入端口:Service接口       │
 *        │  出端口:Repository接口    │
 *        ├──────────────────────────┤
 *        │       领域层(核心)        │
 *        │  Entity / ValueObject    │
 *        │  DomainService           │
 *        ├──────────────────────────┤
 *        │    外部适配器(被驱动侧)    │
 *        │  MySQL / Redis / ES      │
 *        └──────────────────────────┘
 * 
 * 前端对比:
 * 类似于前端的依赖注入和接口抽象
 * 比如Service层定义接口,具体实现可以切换(axios/fetch)
 */

// ===== 端口(Port)=====

// 入端口(Driving Port)—— 外部调用领域的入口
public interface OrderService {
    OrderDTO createOrder(CreateOrderCommand command);
    OrderDTO getOrder(Long orderId);
}

// 出端口(Driven Port)—— 领域调用外部的接口
public interface OrderRepository {
    Order save(Order order);
    Order findById(OrderId id);
}

public interface NotificationPort {
    void sendOrderConfirmation(Order order);
}

// ===== 适配器(Adapter)=====

// 驱动侧适配器
@RestController
public class OrderController implements OrderService {
    @Autowired
    private OrderApplicationService orderService;
    
    @Override
    @PostMapping("/orders")
    public OrderDTO createOrder(@RequestBody CreateOrderRequest request) {
        CreateOrderCommand command = OrderMapper.toCommand(request);
        return orderService.createOrder(command);
    }
}

// 被驱动侧适配器
@Repository
public class MysqlOrderRepository implements OrderRepository {
    @Autowired
    private OrderMapper orderMapper;
    
    @Override
    public Order save(Order order) {
        OrderPO po = OrderConverter.toPO(order);
        orderMapper.insert(po);
        return OrderConverter.toDomain(po);
    }
    
    @Override
    public Order findById(OrderId id) {
        OrderPO po = orderMapper.selectById(id.getValue());
        return OrderConverter.toDomain(po);
    }
}

// 可以方便地切换实现
@Repository
public class MongoOrderRepository implements OrderRepository {
    @Autowired
    private MongoTemplate mongoTemplate;
    
    @Override
    public Order save(Order order) {
        return mongoTemplate.save(order);
    }
    
    @Override
    public Order findById(OrderId id) {
        return mongoTemplate.findById(id.getValue(), Order.class);
    }
}

8.3.4 微服务拆分原则 ​

拆分原则 ​

微服务拆分原则:

1. 单一职责原则(SRP)
   - 每个服务只负责一个业务领域
   - 类比前端:组件单一职责

2. 高内聚低耦合
   - 相关功能放在同一个服务
   - 服务间通过API通信

3. 以业务领域为边界(DDD限界上下文)
   - 电商系统拆分示例:
     ├── 用户服务(注册、登录、用户信息)
     ├── 商品服务(商品管理、分类、库存)
     ├── 订单服务(下单、支付、退款)
     ├── 营销服务(优惠券、活动、积分)
     ├── 支付服务(支付渠道、对账)
     └── 物流服务(物流跟踪、签收)

4. 数据独立
   - 每个服务有自己的数据库
   - 不直接访问其他服务的数据库

5. 渐进式拆分
   - 不要一开始就过度拆分
   - 从单体开始,逐步拆分

Spring Cloud微服务示例 ​

yaml
# Nacos注册中心配置
spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        namespace: dev
        group: DEFAULT_GROUP

# Gateway网关配置
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
java
// Feign远程调用
@FeignClient(name = "user-service")
public interface UserClient {
    
    @GetMapping("/api/users/{id}")
    UserDTO getUserById(@PathVariable Long id);
    
    @GetMapping("/api/users/{id}/vip")
    Boolean isVipUser(@PathVariable Long id);
}

// 使用Feign客户端
@Service
public class OrderService {
    
    @Autowired
    private UserClient userClient;
    
    public OrderDTO createOrder(CreateOrderCommand command) {
        // 调用用户服务获取用户信息
        UserDTO user = userClient.getUserById(command.getUserId());
        
        // 检查VIP
        Boolean isVip = userClient.isVipUser(command.getUserId());
        
        // 创建订单逻辑...
        return orderDTO;
    }
}

// Sentinel熔断降级
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
    @GetMapping("/api/users/{id}")
    UserDTO getUserById(@PathVariable Long id);
}

@Component
public class UserClientFallback implements UserClient {
    @Override
    public UserDTO getUserById(Long id) {
        // 降级逻辑:返回缓存数据或默认值
        return UserDTO.defaultUser();
    }
}

8.3.5 高可用架构设计 ​

核心策略 ​

高可用架构策略:

1. 冗余(Redundancy)
   - 多实例部署
   - 数据库主从复制
   - 类比前端:CDN多节点

2. 熔断(Circuit Breaker)
   - 类比前端:请求超时重试、错误降级
   - 当下游服务异常时快速失败

3. 限流(Rate Limiting)
   - 类比前端:防抖节流
   - 保护系统不被流量冲垮

4. 降级(Degradation)
   - 类比前端:骨架屏、默认数据
   - 非核心功能暂时不可用

5. 异步(Asynchronous)
   - 消息队列解耦
   - 类比前端:Web Worker、异步渲染

熔断器实现 ​

java
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;

import java.time.Duration;

/**
 * Resilience4j熔断器
 * 
 * 前端对比:
 * 类似于Axios的请求重试和超时配置
 * 但熔断器更加智能
 */
public class CircuitBreakerDemo {
    
    // 熔断器配置
    CircuitBreakerConfig config = CircuitBreakerConfig.custom()
        .failureRateThreshold(50)                    // 失败率阈值50%
        .slowCallRateThreshold(80)                   // 慢调用比例阈值80%
        .slowCallDurationThreshold(Duration.ofSeconds(2))  // 慢调用定义:2秒
        .waitDurationInOpenState(Duration.ofSeconds(30))   // 熔断打开后等待30秒
        .permittedNumberOfCallsInHalfOpenState(10)   // 半开状态允许10个请求
        .minimumNumberOfCalls(20)                    // 最少20次调用后才计算失败率
        .build();
    
    CircuitBreaker circuitBreaker = CircuitBreaker.of("userService", config);
    
    // 使用熔断器
    public User getUser(Long userId) {
        return CircuitBreaker.decorateSupplier(circuitBreaker, () -> {
            return userService.getUser(userId);
        }).recover(throwable -> {
            // 降级处理
            return User.defaultUser();
        }).get();
    }
}

/*
 * 熔断器状态机:
 * 
 * CLOSED(关闭)──失败率超阈值──→ OPEN(打开)
 *     ↑                              │
 *     │                         等待30秒
 *     │                              ↓
 *     └──成功率达标──── HALF_OPEN(半开)
 * 
 * CLOSED:正常状态,请求正常通过
 * OPEN:熔断状态,直接返回降级结果
 * HALF_OPEN:探测状态,允许少量请求探测下游是否恢复
 */

8.4 源码阅读(第7-8周) ​

8.4.1 Spring Framework核心源码 ​

Bean生命周期 ​

java
/**
 * Spring Bean生命周期
 * 
 * 实例化 → 属性填充 → 初始化 → 使用 → 销毁
 * 
 * 详细流程:
 * 1. 实例化(Instantiation)
 *    - 通过构造方法创建对象
 *    - 对应前端:new Vue()
 * 
 * 2. 属性填充(Population)
 *    - 依赖注入(@Autowired)
 *    - 对应前端:props传入
 * 
 * 3. 初始化(Initialization)
 *    - Aware接口回调
 *    - BeanPostProcessor前置处理
 *    - @PostConstruct
 *    - InitializingBean.afterPropertiesSet()
 *    - init-method
 *    - BeanPostProcessor后置处理(AOP代理在这里创建)
 *    - 对应前端:mounted钩子
 * 
 * 4. 使用
 *    - 对应前端:组件运行中
 * 
 * 5. 销毁(Destruction)
 *    - @PreDestroy
 *    - DisposableBean.destroy()
 *    - destroy-method
 *    - 对应前端:beforeUnmount钩子
 */

@Component
public class MyBean implements InitializingBean, DisposableBean, BeanNameAware, ApplicationContextAware {
    
    private String name;
    
    // 1. 构造方法
    public MyBean() {
        System.out.println("1. 构造方法");
    }
    
    // 2. Aware接口回调
    @Override
    public void setBeanName(String name) {
        System.out.println("2. BeanNameAware: " + name);
    }
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("3. ApplicationContextAware");
    }
    
    // 3. BeanPostProcessor前置处理(框架内部)
    
    // 4. @PostConstruct
    @PostConstruct
    public void postConstruct() {
        System.out.println("4. @PostConstruct");
    }
    
    // 5. InitializingBean
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("5. afterPropertiesSet");
    }
    
    // 6. 自定义init-method(XML配置)
    public void customInit() {
        System.out.println("6. customInit");
    }
    
    // 7. BeanPostProcessor后置处理(AOP代理创建)
    
    // 8. 使用阶段
    
    // 9. @PreDestroy
    @PreDestroy
    public void preDestroy() {
        System.out.println("7. @PreDestroy");
    }
    
    // 10. DisposableBean
    @Override
    public void destroy() throws Exception {
        System.out.println("8. destroy");
    }
}

/*
 * 执行顺序:
 * 1. 构造方法
 * 2. BeanNameAware: myBean
 * 3. ApplicationContextAware
 * 4. @PostConstruct
 * 5. afterPropertiesSet
 * 6. customInit
 * 7. @PreDestroy
 * 8. destroy
 */

Spring事务源码核心 ​

java
/**
 * Spring事务传播机制
 * 
 * 前端对比:
 * 类似于Promise链中的错误处理
 * 或者组件树中的事件冒泡
 */

@Service
public class TransactionDemo {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private LogService logService;
    
    // REQUIRED(默认):有事务就加入,没有就新建
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        // 在事务中执行
        userService.updateUser();
    }
    
    // REQUIRES_NEW:总是新建事务,挂起当前事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // 独立事务,不受外部事务影响
        logService.addLog("操作日志");
    }
    
    // NESTED:嵌套事务(基于Savepoint)
    @Transactional(propagation = Propagation.NESTED)
    public void methodC() {
        // 如果外部事务回滚,嵌套事务也回滚
        // 但嵌套事务回滚,不影响外部事务
    }
    
    // 实际场景:下单流程
    @Transactional
    public void placeOrder(OrderRequest request) {
        try {
            // 创建订单(主事务)
            createOrder(request);
            
            // 扣减库存(加入当前事务)
            deductStock(request);
            
            // 记录日志(独立事务,REQUIRES_NEW)
            // 即使订单失败,日志也要记录
            logService.addLog("下单操作");
            
        } catch (Exception e) {
            // 订单和库存一起回滚
            // 但日志不回滚(独立事务)
            throw new BusinessException("下单失败", e);
        }
    }
}

8.4.2 Spring Boot自动配置源码 ​

自动配置原理 ​

java
/**
 * Spring Boot自动配置原理
 * 
 * 核心注解链:
 * @SpringBootApplication
 *   ├── @SpringBootConfiguration → @Configuration
 *   ├── @EnableAutoConfiguration → @Import(AutoConfigurationImportSelector.class)
 *   └── @ComponentScan
 * 
 * 自动配置流程:
 * 1. @EnableAutoConfiguration触发AutoConfigurationImportSelector
 * 2. 读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
 *    (JDK 8及之前是 META-INF/spring.factories)
 * 3. 获取所有自动配置类
 * 4. 通过条件注解过滤(@ConditionalOnClass, @ConditionalOnProperty等)
 * 5. 加载符合条件的配置类
 * 
 * 前端对比:
 * 类似于webpack/vite的插件系统
 * 通过配置文件自动加载和注册插件
 */

// ===== 自定义自动配置 =====

// 1. 定义配置属性
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
    private String prefix = "[MyService]";
    private String suffix = "";
    private boolean enabled = true;
    
    // getter/setter
}

// 2. 定义服务类
public class MyService {
    private MyServiceProperties properties;
    
    public MyService(MyServiceProperties properties) {
        this.properties = properties;
    }
    
    public String wrap(String content) {
        return properties.getPrefix() + content + properties.getSuffix();
    }
}

// 3. 自动配置类
@AutoConfiguration
@ConditionalOnClass(MyService.class)  // classpath下有这个类才生效
@ConditionalOnProperty(prefix = "my.service", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean  // 容器中不存在时才创建
    public MyService myService(MyServiceProperties properties) {
        return new MyService(properties);
    }
}

// 4. 注册自动配置
// 在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中添加:
// com.example.MyServiceAutoConfiguration

// 5. 使用方只需引入依赖并配置
// application.yml:
// my:
//   service:
//     prefix: "[Hello]"
//     suffix: "[World]"

8.4.3 MyBatis源码 ​

核心执行流程 ​

java
/**
 * MyBatis核心执行流程
 * 
 * 前端对比:
 * 类似于Axios请求拦截器链
 * Configuration → 拦截器链 → 执行器 → 结果处理
 * 
 * 核心组件:
 * 1. SqlSessionFactory:创建SqlSession的工厂
 * 2. SqlSession:与数据库交互的会话
 * 3. Executor:执行器(Simple/Reuse/Batch)
 * 4. StatementHandler:SQL语句处理器
 * 5. ParameterHandler:参数处理器
 * 6. ResultSetHandler:结果集处理器
 * 7. Interceptor:拦截器(插件机制)
 * 
 * 执行流程:
 * SqlSession → Executor → StatementHandler → ParameterHandler → JDBC
 *                                    ↓
 *                            ResultSetHandler → 返回结果
 */

// ===== MyBatis插件原理 =====
// MyBatis允许拦截四大对象的方法:
// 1. Executor(update, query, commit, rollback等)
// 2. StatementHandler(prepare, parameterize, batch, update, query等)
// 3. ParameterHandler(setParameter)
// 4. ResultSetHandler(handleResultSets)

// 示例:分页插件原理
@Intercepts({
    @Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = {Connection.class, Integer.class}
    )
})
public class PageInterceptor implements Interceptor {
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1. 获取StatementHandler
        StatementHandler handler = (StatementHandler) invocation.getTarget();
        
        // 2. 获取原始SQL
        BoundSql boundSql = handler.getBoundSql();
        String originalSql = boundSql.getSql();
        
        // 3. 获取分页参数
        Page<?> page = PageContext.getCurrentPage();
        
        if (page != null) {
            // 4. 改写SQL(添加LIMIT)
            String pageSql = originalSql + " LIMIT " + page.getOffset() + ", " + page.getSize();
            
            // 5. 通过反射修改SQL
            ReflectUtil.setFieldValue(boundSql, "sql", pageSql);
        }
        
        // 6. 执行原方法
        return invocation.proceed();
    }
}

8.4.4 源码阅读方法 ​

源码阅读方法论:

1. 带着问题读
   - 不要从头到尾逐行读
   - 先有一个具体问题,然后追踪源码
   - 例如:@Autowired是怎么注入的?

2. 从断点开始
   - 在关键位置打断点
   - Debug模式跟踪调用链
   - 类比前端:Chrome DevTools断点调试

3. 关注核心流程
   - Spring:Bean生命周期、AOP实现、事务管理
   - MyBatis:SQL执行流程、插件机制
   - Spring Boot:自动配置原理

4. 画图辅助
   - 类图:理解类的继承关系
   - 时序图:理解方法调用顺序
   - 流程图:理解核心逻辑

5. 推荐源码阅读顺序
   第一阶段:
   - Spring Bean生命周期
   - Spring AOP实现
   - Spring MVC请求处理流程
   
   第二阶段:
   - Spring Boot自动配置
   - Spring事务实现
   - MyBatis插件机制
   
   第三阶段:
   - Spring循环依赖解决(三级缓存)
   - Spring Cloud网关路由
   - Netty线程模型

8.5 练习任务 ​

练习1:JVM调优实战 ​

  • 编写一个内存泄漏的程序(不断创建对象不释放)
  • 使用jmap导出堆dump
  • 使用MAT分析内存泄漏原因
  • 调整JVM参数观察GC行为变化

练习2:SQL优化实战 ​

  • 创建百万级数据的测试表
  • 使用EXPLAIN分析慢查询
  • 优化索引设计
  • 对比优化前后的查询时间

练习3:缓存系统设计 ​

  • 实现一个完整的缓存层
  • 处理缓存穿透、击穿、雪崩问题
  • 实现缓存一致性(延迟双删策略)
  • 编写缓存监控接口

练习4:设计模式实践 ​

  • 用策略模式实现多种排序算法切换
  • 用观察者模式实现事件总线
  • 用建造者模式实现复杂查询构建器
  • 用代理模式实现方法执行时间统计

练习5:DDD建模 ​

  • 设计一个图书管理系统的领域模型
  • 实现实体、值对象、聚合根
  • 划分限界上下文
  • 实现仓储模式

练习6:源码阅读 ​

  • Debug跟踪Spring Bean创建过程
  • 阅读Spring MVC请求分发流程
  • 分析MyBatis-SQL执行链路
  • 理解Spring Boot自动配置加载过程

8.6 学习检查清单 ​

JVM相关 ​

  • [ ] 理解JVM内存模型各区域的作用
  • [ ] 掌握垃圾回收算法(标记-清除、复制、标记-整理)
  • [ ] 了解主流垃圾收集器的特点和适用场景
  • [ ] 理解双亲委派模型及打破方式
  • [ ] 能使用JVM调优参数配置堆内存和GC
  • [ ] 熟练使用jps、jstat、jstack、jmap排查问题
  • [ ] 掌握Arthas基本使用

性能优化相关 ​

  • [ ] 能使用EXPLAIN分析SQL执行计划
  • [ ] 掌握索引优化原则和失效场景
  • [ ] 理解连接池参数调优
  • [ ] 掌握缓存策略(穿透、击穿、雪崩解决方案)
  • [ ] 了解Spring WebFlux响应式编程
  • [ ] 掌握限流、熔断、降级的实现

架构设计相关 ​

  • [ ] 掌握常用设计模式(单例、工厂、代理、策略、观察者)
  • [ ] 理解DDD核心概念(实体、值对象、聚合根、限界上下文)
  • [ ] 了解六边形架构的分层思想
  • [ ] 理解微服务拆分原则
  • [ ] 掌握高可用架构设计策略

源码阅读相关 ​

  • [ ] 理解Spring Bean生命周期
  • [ ] 理解Spring AOP实现原理
  • [ ] 理解Spring Boot自动配置原理
  • [ ] 理解MyBatis核心执行流程
  • [ ] 掌握源码阅读方法论

完成本阶段后,你应该具备Java进阶能力,能够进行性能调优、架构设计,并具备源码阅读能力。这是一个持续学习的过程,需要在实际项目中不断积累和深化理解。

评论

正在加载评论…