从 .NET 到 Java 的转型指南:详细学习路线与实践建议

文章目录

    • 第一部分:转型背景与核心差异分析
      • [1.1 为什么需要从 .NET 转型到 Java](#1.1 为什么需要从 .NET 转型到 Java)
      • [1.2 .NET 与 Java 核心架构差异](#1.2 .NET 与 Java 核心架构差异)
        • [1.2.1 运行时环境对比](#1.2.1 运行时环境对比)
        • [1.2.2 内存管理机制](#1.2.2 内存管理机制)
      • [1.3 心态调整与学习策略](#1.3 心态调整与学习策略)
        • [1.3.1 相似性利用](#1.3.1 相似性利用)
        • [1.3.2 差异性重视](#1.3.2 差异性重视)
    • [第二部分:Java 语言基础深入学习](#第二部分:Java 语言基础深入学习)
      • [2.1 Java 语法核心概念](#2.1 Java 语法核心概念)
        • [2.1.1 基本数据类型与包装类](#2.1.1 基本数据类型与包装类)
        • [2.1.2 字符串处理](#2.1.2 字符串处理)
      • [2.2 面向对象编程深入](#2.2 面向对象编程深入)
        • [2.2.1 类与继承](#2.2.1 类与继承)
        • [2.2.2 访问控制与封装](#2.2.2 访问控制与封装)
      • [2.3 异常处理机制](#2.3 异常处理机制)
      • [2.4 集合框架](#2.4 集合框架)
    • [第三部分:Java 生态系统与工具链](#第三部分:Java 生态系统与工具链)
      • [3.1 构建工具:Maven 与 Gradle](#3.1 构建工具:Maven 与 Gradle)
        • [3.1.1 Maven 详细配置](#3.1.1 Maven 详细配置)
        • [3.1.2 Gradle 构建脚本](#3.1.2 Gradle 构建脚本)
      • [3.2 开发工具与环境配置](#3.2 开发工具与环境配置)
        • [3.2.1 IDE 选择与配置](#3.2.1 IDE 选择与配置)
        • [3.2.2 版本控制集成](#3.2.2 版本控制集成)
      • [3.3 测试框架](#3.3 测试框架)
        • [3.3.1 JUnit 测试](#3.3.1 JUnit 测试)
        • [3.3.2 Mockito 模拟测试](#3.3.2 Mockito 模拟测试)
    • [第四部分:Spring 框架深入学习](#第四部分:Spring 框架深入学习)
      • [4.1 Spring Core 核心概念](#4.1 Spring Core 核心概念)
        • [4.1.1 依赖注入与控制反转](#4.1.1 依赖注入与控制反转)
        • [4.1.2 AOP 面向切面编程](#4.1.2 AOP 面向切面编程)
      • [4.2 Spring Boot 自动化配置](#4.2 Spring Boot 自动化配置)
        • [4.2.1 Spring Boot 应用启动](#4.2.1 Spring Boot 应用启动)
        • [4.2.2 应用配置文件](#4.2.2 应用配置文件)
      • [4.3 Spring MVC Web 开发](#4.3 Spring MVC Web 开发)
        • [4.3.1 RESTful API 开发](#4.3.1 RESTful API 开发)
    • 第五部分:数据库与持久层技术
      • [5.1 JPA 与 Hibernate](#5.1 JPA 与 Hibernate)
        • [5.1.1 实体类映射](#5.1.1 实体类映射)
        • [5.1.2 Repository 数据访问层](#5.1.2 Repository 数据访问层)
      • [5.2 数据库迁移与版本控制](#5.2 数据库迁移与版本控制)
        • [5.2.1 Flyway 数据库迁移](#5.2.1 Flyway 数据库迁移)
        • [5.2.2 Flyway 配置](#5.2.2 Flyway 配置)
    • 第六部分:高级主题与最佳实践
      • [6.1 性能优化与缓存](#6.1 性能优化与缓存)
        • [6.1.1 Redis 缓存集成](#6.1.1 Redis 缓存集成)
      • [6.2 安全与认证授权](#6.2 安全与认证授权)
        • [6.2.1 Spring Security 配置](#6.2.1 Spring Security 配置)
      • [6.3 微服务与分布式系统](#6.3 微服务与分布式系统)
        • [6.3.1 Spring Cloud 微服务配置](#6.3.1 Spring Cloud 微服务配置)
    • 第七部分:部署与运维
      • [7.1 Docker 容器化部署](#7.1 Docker 容器化部署)
        • [7.1.1 Dockerfile 配置](#7.1.1 Dockerfile 配置)
        • [7.1.2 Docker Compose 配置](#7.1.2 Docker Compose 配置)
      • [7.2 监控与日志](#7.2 监控与日志)
        • [7.2.1 Spring Boot Actuator 配置](#7.2.1 Spring Boot Actuator 配置)
        • [7.2.2 自定义健康检查](#7.2.2 自定义健康检查)
    • 第八部分:学习路线与持续提升
    • 总结

第一部分:转型背景与核心差异分析

1.1 为什么需要从 .NET 转型到 Java

在当前的技术环境中,从 .NET 转型到 Java 通常基于以下考虑:

  1. 跨平台需求增加:Java 的"一次编写,到处运行"特性在云原生和容器化环境中具有优势
  2. 生态系统丰富性:Java 拥有庞大的开源生态系统和社区支持
  3. 成本考量:Java 开源技术栈可以降低许可成本
  4. 人才市场因素:Java 开发者在全球范围内更为普及
  5. 企业级应用成熟度:Java 在企业级应用和大规模系统中有着深厚的积累

1.2 .NET 与 Java 核心架构差异

1.2.1 运行时环境对比

.NET CLR vs JVM

csharp 复制代码
// .NET 中的类型系统示例
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public virtual void Display()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}
java 复制代码
// Java 中的对应实现
public class Person {
    private String name;
    private int age;
    
    public Person() {}
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    
    public void display() {
        System.out.println("Name: " + name + ", Age: " + age);
    }
}

主要差异

  • Java 中没有属性语法,使用 getter/setter 方法
  • Java 方法默认是虚方法(virtual),而 C# 需要显式声明
  • Java 包机制与 .NET 命名空间有相似性但实现不同
1.2.2 内存管理机制

.NET GC vs Java GC

csharp 复制代码
// .NET 中的资源管理
public class ResourceHandler : IDisposable
{
    private bool disposed = false;
    
    public void ProcessData()
    {
        if (disposed)
            throw new ObjectDisposedException(nameof(ResourceHandler));
        
        // 处理逻辑
    }
    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // 释放托管资源
            }
            // 释放非托管资源
            disposed = true;
        }
    }
    
    ~ResourceHandler()
    {
        Dispose(false);
    }
}
java 复制代码
// Java 中的资源管理
public class ResourceHandler implements AutoCloseable {
    private boolean closed = false;
    
    public void processData() {
        if (closed) {
            throw new IllegalStateException("ResourceHandler is closed");
        }
        // 处理逻辑
    }
    
    @Override
    public void close() {
        if (!closed) {
            // 释放资源
            closed = true;
        }
    }
    
    // Java 7+ 的 try-with-resources
    public static void main(String[] args) {
        try (ResourceHandler handler = new ResourceHandler()) {
            handler.processData();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1.3 心态调整与学习策略

1.3.1 相似性利用
  • 面向对象编程概念相通
  • 设计模式应用基本一致
  • 软件开发原则通用
1.3.2 差异性重视
  • 平台特性差异
  • 生态系统工具链
  • 部署和运维方式

第二部分:Java 语言基础深入学习

2.1 Java 语法核心概念

2.1.1 基本数据类型与包装类
java 复制代码
// Java 基本数据类型与包装类
public class DataTypesExample {
    public static void main(String[] args) {
        // 基本数据类型
        byte byteValue = 127;
        short shortValue = 32767;
        int intValue = 2147483647;
        long longValue = 9223372036854775807L; // 注意 L 后缀
        float floatValue = 3.14f; // 注意 f 后缀
        double doubleValue = 3.141592653589793;
        char charValue = 'A';
        boolean booleanValue = true;
        
        // 对应的包装类
        Byte byteObj = byteValue;
        Short shortObj = shortValue;
        Integer intObj = intValue;
        Long longObj = longValue;
        Float floatObj = floatValue;
        Double doubleObj = doubleValue;
        Character charObj = charValue;
        Boolean booleanObj = booleanValue;
        
        // 自动装箱和拆箱
        Integer autoBoxed = 100; // 自动装箱
        int autoUnboxed = autoBoxed; // 自动拆箱
        
        // 与 .NET 对比:Java 有明确的原始类型和包装类型区分
    }
}
2.1.2 字符串处理
java 复制代码
public class StringHandling {
    public static void main(String[] args) {
        // 字符串创建
        String str1 = "Hello";
        String str2 = new String("World");
        
        // 字符串不可变性
        String original = "Java";
        String modified = original.concat(" Programming");
        System.out.println(original); // 输出: Java
        System.out.println(modified); // 输出: Java Programming
        
        // StringBuilder 用于可变字符串操作
        StringBuilder sb = new StringBuilder();
        sb.append("Hello");
        sb.append(" ");
        sb.append("World");
        String result = sb.toString();
        System.out.println(result); // 输出: Hello World
        
        // StringBuffer 线程安全版本
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("Thread");
        stringBuffer.append("Safe");
        
        // 与 C# 对比:Java 字符串也是不可变的,但 Java 有 StringBuilder/StringBuffer
    }
}

2.2 面向对象编程深入

2.2.1 类与继承
java 复制代码
// 基类
public abstract class Animal {
    protected String name;
    protected int age;
    
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // 抽象方法
    public abstract void makeSound();
    
    // 具体方法
    public void sleep() {
        System.out.println(name + " is sleeping");
    }
    
    // final 方法,不能被子类重写
    public final void breathe() {
        System.out.println(name + " is breathing");
    }
}

// 接口
public interface Pet {
    void play();
    String getOwner();
}

// 继承与实现
public class Dog extends Animal implements Pet {
    private String owner;
    
    public Dog(String name, int age, String owner) {
        super(name, age); // 调用父类构造函数
        this.owner = owner;
    }
    
    @Override
    public void makeSound() {
        System.out.println("Woof! Woof!");
    }
    
    @Override
    public void play() {
        System.out.println(name + " is playing with " + owner);
    }
    
    @Override
    public String getOwner() {
        return owner;
    }
    
    // 静态方法
    public static void describe() {
        System.out.println("Dogs are loyal animals");
    }
}
2.2.2 访问控制与封装
java 复制代码
public class AccessModifiersExample {
    // private - 仅当前类可见
    private String privateField = "private";
    
    // default (package-private) - 同包可见
    String defaultField = "default";
    
    // protected - 同包和子类可见
    protected String protectedField = "protected";
    
    // public - 所有类可见
    public String publicField = "public";
    
    // getter 和 setter 方法
    public String getPrivateField() {
        return privateField;
    }
    
    public void setPrivateField(String value) {
        this.privateField = value;
    }
}

2.3 异常处理机制

java 复制代码
public class ExceptionHandling {
    
    // 检查型异常
    public void readFile(String filename) throws IOException {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(filename));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (FileNotFoundException e) {
            System.err.println("File not found: " + filename);
            throw e; // 重新抛出异常
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
            throw e;
        } finally {
            // 确保资源被释放
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    System.err.println("Error closing reader: " + e.getMessage());
                }
            }
        }
    }
    
    // 使用 try-with-resources (Java 7+)
    public void readFileModern(String filename) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } // 自动调用 reader.close()
    }
    
    // 非检查型异常
    public void divideNumbers(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("Divisor cannot be zero");
        }
        int result = a / b;
        System.out.println("Result: " + result);
    }
    
    // 自定义异常
    public static class CustomException extends Exception {
        public CustomException(String message) {
            super(message);
        }
        
        public CustomException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}

2.4 集合框架

java 复制代码
import java.util.*;
import java.util.stream.Collectors;

public class CollectionExamples {
    
    public void listExamples() {
        // ArrayList - 类似 C# List<T>
        List<String> arrayList = new ArrayList<>();
        arrayList.add("Apple");
        arrayList.add("Banana");
        arrayList.add("Orange");
        
        // LinkedList
        List<String> linkedList = new LinkedList<>();
        linkedList.add("First");
        linkedList.add("Second");
        
        // 遍历方式
        for (String fruit : arrayList) {
            System.out.println(fruit);
        }
        
        // 使用迭代器
        Iterator<String> iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        
        // Java 8+ Stream API
        arrayList.stream()
                .filter(f -> f.startsWith("A"))
                .forEach(System.out::println);
    }
    
    public void setExamples() {
        // HashSet - 无序,不允许重复
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Apple");
        hashSet.add("Banana");
        hashSet.add("Apple"); // 不会被添加
        
        // TreeSet - 有序
        Set<String> treeSet = new TreeSet<>();
        treeSet.add("Orange");
        treeSet.add("Apple");
        treeSet.add("Banana");
        // 输出顺序: Apple, Banana, Orange
    }
    
    public void mapExamples() {
        // HashMap - 类似 C# Dictionary<TKey, TValue>
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("John", 25);
        hashMap.put("Jane", 30);
        hashMap.put("Bob", 35);
        
        // 获取值
        Integer age = hashMap.get("John");
        
        // 遍历
        for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        
        // Java 8+ 方式
        hashMap.forEach((key, value) -> System.out.println(key + ": " + value));
        
        // TreeMap - 按键排序
        Map<String, Integer> treeMap = new TreeMap<>();
        treeMap.put("Orange", 5);
        treeMap.put("Apple", 3);
        treeMap.put("Banana", 7);
        // 按键顺序: Apple, Banana, Orange
    }
    
    public void streamExamples() {
        List<String> names = Arrays.asList("John", "Jane", "Bob", "Alice", "Charlie");
        
        // Stream 操作
        List<String> result = names.stream()
                .filter(name -> name.length() > 4)
                .map(String::toUpperCase)
                .sorted()
                .collect(Collectors.toList());
        
        System.out.println(result); // [ALICE, CHARLIE]
        
        // 分组
        Map<Integer, List<String>> groupedByLength = names.stream()
                .collect(Collectors.groupingBy(String::length));
        
        System.out.println(groupedByLength);
    }
}

第三部分:Java 生态系统与工具链

3.1 构建工具:Maven 与 Gradle

3.1.1 Maven 详细配置
xml 复制代码
<!-- pom.xml 示例 -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <!-- 项目坐标 -->
    <groupId>com.example</groupId>
    <artifactId>my-java-app</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>5.3.9</spring.version>
    </properties>
    
    <dependencies>
        <!-- Spring Framework -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        
        <!-- 测试依赖 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        
        <!-- 日志 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.32</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <!-- 编译器插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
            
            <!-- 打包插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.example.MainApplication</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
3.1.2 Gradle 构建脚本
groovy 复制代码
// build.gradle 示例
plugins {
    id 'java'
    id 'application'
}

group = 'com.example'
version = '1.0.0'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework:spring-context:5.3.9'
    implementation 'org.slf4j:slf4j-api:1.7.32'
    
    testImplementation 'junit:junit:4.13.2'
}

application {
    mainClassName = 'com.example.MainApplication'
}

// 自定义任务
task createStartScripts(type: CreateStartScripts) {
    mainClass = 'com.example.MainApplication'
    applicationName = 'myapp'
    outputDir = new File(project.buildDir, 'scripts')
    classpath = jar.outputs.files + project.configurations.runtimeClasspath
}

// 测试配置
test {
    useJUnit()
    testLogging {
        events "passed", "skipped", "failed"
        exceptionFormat "full"
    }
}

3.2 开发工具与环境配置

3.2.1 IDE 选择与配置

IntelliJ IDEA 推荐配置

  • 安装 Lombok 插件
  • 配置 Code Style 符合团队规范
  • 启用 Annotation Processors
  • 配置 Debug 和 Hot Swap

Eclipse 配置

  • 安装 Spring Tools Suite
  • 配置 Build Path
  • 设置 Formatter 和 Clean Up
3.2.2 版本控制集成
java 复制代码
// Git 版本控制最佳实践示例
public class VersionControlExample {
    /**
     * 功能:用户注册
     * 作者:张三
     * 日期:2023-10-01
     * 版本:1.0
     */
    public void registerUser(String username, String password) {
        // 参数验证
        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        
        if (password == null || password.length() < 6) {
            throw new IllegalArgumentException("密码长度至少6位");
        }
        
        // 业务逻辑
        // ...
    }
    
    /**
     * 功能:用户登录
     * 修改:李四 - 2023-10-02 - 添加记住登录状态功能
     */
    public boolean loginUser(String username, String password, boolean rememberMe) {
        // 登录逻辑
        return true;
    }
}

3.3 测试框架

3.3.1 JUnit 测试
java 复制代码
import org.junit.*;
import static org.junit.Assert.*;

public class CalculatorTest {
    
    private Calculator calculator;
    
    @BeforeClass
    public static void setUpClass() {
        // 在所有测试方法之前执行一次
        System.out.println("测试类初始化");
    }
    
    @AfterClass
    public static void tearDownClass() {
        // 在所有测试方法之后执行一次
        System.out.println("测试类清理");
    }
    
    @Before
    public void setUp() {
        // 在每个测试方法之前执行
        calculator = new Calculator();
    }
    
    @After
    public void tearDown() {
        // 在每个测试方法之后执行
        calculator = null;
    }
    
    @Test
    public void testAdd() {
        // 准备
        int a = 5;
        int b = 3;
        int expected = 8;
        
        // 执行
        int actual = calculator.add(a, b);
        
        // 验证
        assertEquals("加法计算错误", expected, actual);
    }
    
    @Test
    public void testDivide() {
        // 测试正常除法
        assertEquals(2, calculator.divide(6, 3));
    }
    
    @Test(expected = IllegalArgumentException.class)
    public void testDivideByZero() {
        // 测试除零异常
        calculator.divide(5, 0);
    }
    
    @Test
    @Ignore("尚未实现")
    public void testAdvancedFeature() {
        // 被忽略的测试
    }
    
    // 参数化测试
    @RunWith(Parameterized.class)
    public static class ParameterizedTest {
        private int input;
        private int expected;
        private Calculator calculator = new Calculator();
        
        public ParameterizedTest(int input, int expected) {
            this.input = input;
            this.expected = expected;
        }
        
        @Parameterized.Parameters
        public static Collection<Object[]> data() {
            return Arrays.asList(new Object[][] {
                {0, 0}, {1, 1}, {2, 4}, {3, 9}
            });
        }
        
        @Test
        public void testSquare() {
            assertEquals(expected, calculator.square(input));
        }
    }
}

// 被测试的类
class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    
    public int divide(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("除数不能为零");
        }
        return a / b;
    }
    
    public int square(int x) {
        return x * x;
    }
}
3.3.2 Mockito 模拟测试
java 复制代码
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
    
    @Mock
    private UserRepository userRepository;
    
    @Mock
    private EmailService emailService;
    
    @InjectMocks
    private UserService userService;
    
    @Test
    public void testRegisterUser() {
        // 准备模拟数据
        User user = new User("john@example.com", "John Doe");
        when(userRepository.save(any(User.class))).thenReturn(user);
        doNothing().when(emailService).sendWelcomeEmail(anyString());
        
        // 执行测试
        User registeredUser = userService.registerUser("john@example.com", "John Doe");
        
        // 验证行为
        verify(userRepository, times(1)).save(any(User.class));
        verify(emailService, times(1)).sendWelcomeEmail("john@example.com");
        
        // 验证结果
        assertNotNull(registeredUser);
        assertEquals("john@example.com", registeredUser.getEmail());
    }
    
    @Test
    public void testRegisterUserWithExistingEmail() {
        // 模拟已存在的用户
        when(userRepository.findByEmail("existing@example.com"))
            .thenReturn(new User("existing@example.com", "Existing User"));
        
        // 验证异常
        try {
            userService.registerUser("existing@example.com", "New User");
            fail("应该抛出异常");
        } catch (IllegalArgumentException e) {
            assertEquals("邮箱已存在", e.getMessage());
        }
        
        // 验证保存方法没有被调用
        verify(userRepository, never()).save(any(User.class));
    }
    
    @Captor
    private ArgumentCaptor<User> userCaptor;
    
    @Test
    public void testUserRegistrationDetails() {
        // 执行测试
        userService.registerUser("test@example.com", "Test User");
        
        // 捕获参数并验证
        verify(userRepository).save(userCaptor.capture());
        User capturedUser = userCaptor.getValue();
        
        assertEquals("test@example.com", capturedUser.getEmail());
        assertEquals("Test User", capturedUser.getName());
        assertNotNull(capturedUser.getRegistrationDate());
    }
}

// 相关类定义
class User {
    private String email;
    private String name;
    private Date registrationDate;
    
    public User(String email, String name) {
        this.email = email;
        this.name = name;
        this.registrationDate = new Date();
    }
    
    // getters and setters
}

interface UserRepository {
    User save(User user);
    User findByEmail(String email);
}

interface EmailService {
    void sendWelcomeEmail(String email);
}

class UserService {
    private UserRepository userRepository;
    private EmailService emailService;
    
    public User registerUser(String email, String name) {
        // 检查邮箱是否已存在
        User existing = userRepository.findByEmail(email);
        if (existing != null) {
            throw new IllegalArgumentException("邮箱已存在");
        }
        
        // 创建新用户
        User newUser = new User(email, name);
        User savedUser = userRepository.save(newUser);
        
        // 发送欢迎邮件
        emailService.sendWelcomeEmail(email);
        
        return savedUser;
    }
}

第四部分:Spring 框架深入学习

4.1 Spring Core 核心概念

4.1.1 依赖注入与控制反转
java 复制代码
// 配置类方式
@Configuration
@ComponentScan("com.example")
@PropertySource("classpath:application.properties")
public class AppConfig {
    
    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:schema.sql")
                .addScript("classpath:data.sql")
                .build();
    }
    
    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        dataSource.setUsername("user");
        dataSource.setPassword("password");
        return dataSource;
    }
    
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

// 服务类
@Service
@Transactional
public class UserService {
    
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    // 构造器注入(推荐)
    @Autowired
    public UserService(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
    
    public User registerUser(UserRegistrationDto dto) {
        // 业务逻辑
        User user = new User();
        user.setEmail(dto.getEmail());
        user.setName(dto.getName());
        user.setPassword(encodePassword(dto.getPassword()));
        
        User savedUser = userRepository.save(user);
        emailService.sendWelcomeEmail(savedUser.getEmail());
        
        return savedUser;
    }
    
    private String encodePassword(String password) {
        // 密码加密逻辑
        return password; // 实际应该使用 BCrypt 等
    }
}

// 数据访问层
@Repository
public class JpaUserRepository implements UserRepository {
    
    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public User save(User user) {
        if (user.getId() == null) {
            entityManager.persist(user);
            return user;
        } else {
            return entityManager.merge(user);
        }
    }
    
    @Override
    public Optional<User> findByEmail(String email) {
        TypedQuery<User> query = entityManager.createQuery(
            "SELECT u FROM User u WHERE u.email = :email", User.class);
        query.setParameter("email", email);
        
        try {
            return Optional.of(query.getSingleResult());
        } catch (NoResultException e) {
            return Optional.empty();
        }
    }
}

// 邮件服务
@Service
public class EmailService {
    
    private final JavaMailSender mailSender;
    private final TemplateEngine templateEngine;
    
    @Autowired
    public EmailService(JavaMailSender mailSender, TemplateEngine templateEngine) {
        this.mailSender = mailSender;
        this.templateEngine = templateEngine;
    }
    
    @Async
    public void sendWelcomeEmail(String email) {
        try {
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            
            helper.setTo(email);
            helper.setSubject("欢迎注册");
            
            Context context = new Context();
            context.setVariable("email", email);
            String htmlContent = templateEngine.process("welcome-email", context);
            
            helper.setText(htmlContent, true);
            mailSender.send(message);
            
        } catch (Exception e) {
            throw new RuntimeException("发送邮件失败", e);
        }
    }
}
4.1.2 AOP 面向切面编程
java 复制代码
// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}

// 切面类
@Aspect
@Component
public class LoggingAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        Object proceed = joinPoint.proceed();
        
        long executionTime = System.currentTimeMillis() - start;
        
        logger.info("{} executed in {} ms", 
                   joinPoint.getSignature(), executionTime);
        
        return proceed;
    }
    
    // 异常处理切面
    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", 
                   throwing = "ex")
    public void logServiceException(Exception ex) {
        logger.error("Service layer exception: {}", ex.getMessage(), ex);
    }
    
    // 方法调用前后通知
    @Before("execution(* com.example.service.UserService.*(..))")
    public void logMethodCall(JoinPoint joinPoint) {
        logger.debug("调用方法: {} with args: {}", 
                    joinPoint.getSignature().getName(),
                    Arrays.toString(joinPoint.getArgs()));
    }
}

// 使用切面的服务
@Service
public class OrderService {
    
    @LogExecutionTime
    public Order createOrder(OrderRequest request) {
        // 业务逻辑
        try {
            Thread.sleep(100); // 模拟处理时间
            return new Order();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }
}

4.2 Spring Boot 自动化配置

4.2.1 Spring Boot 应用启动
java 复制代码
// 主应用类
@SpringBootApplication
@EnableAsync
@EnableScheduling
@EnableCaching
@EnableTransactionManagement
public class Application {
    
    private static final Logger logger = LoggerFactory.getLogger(Application.class);
    
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        
        // 自定义启动配置
        app.setBannerMode(Banner.Mode.CONSOLE);
        app.setLogStartupInfo(true);
        
        ConfigurableApplicationContext context = app.run(args);
        
        // 应用启动后执行
        checkBeans(context);
    }
    
    private static void checkBeans(ConfigurableApplicationContext context) {
        logger.info("检查Spring Bean配置...");
        
        String[] beanNames = context.getBeanDefinitionNames();
        Arrays.sort(beanNames);
        
        for (String beanName : beanNames) {
            if (beanName.contains("service") || beanName.contains("controller")) {
                logger.debug("加载的Bean: {}", beanName);
            }
        }
    }
}

// 配置类
@Configuration
@EnableConfigurationProperties({AppProperties.class, SecurityProperties.class})
public class AppConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        mapper.registerModule(new JavaTimeModule());
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        return mapper;
    }
    
    @Bean
    @ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("users", "orders");
    }
}

// 配置属性类
@ConfigurationProperties(prefix = "app")
@Component
@Validated
public class AppProperties {
    
    @NotBlank
    private String name;
    
    @Min(1024)
    @Max(65535)
    private int port = 8080;
    
    private Security security = new Security();
    
    // getters and setters
    
    public static class Security {
        private boolean enabled = true;
        private String secretKey;
        
        // getters and setters
    }
}
4.2.2 应用配置文件
yaml 复制代码
# application.yml
spring:
  application:
    name: my-spring-app
  profiles:
    active: @activatedProperties@
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: ${DB_USERNAME:root}
    password: ${DB_PASSWORD:password}
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect
        format_sql: true
  redis:
    host: localhost
    port: 6379
    timeout: 2000ms

server:
  port: 8080
  servlet:
    context-path: /api
  compression:
    enabled: true

app:
  name: "My Application"
  port: 8080
  security:
    enabled: true
    secret-key: ${APP_SECRET:default-secret-key}
  cache:
    enabled: true

logging:
  level:
    com.example: DEBUG
    org.springframework.security: INFO
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} - %logger{36} - %msg%n"
  file:
    name: logs/application.log
    max-size: 10MB

4.3 Spring MVC Web 开发

4.3.1 RESTful API 开发
java 复制代码
// 统一响应格式
public class ApiResponse<T> {
    private boolean success;
    private String message;
    private T data;
    private String timestamp;
    
    public ApiResponse(boolean success, String message, T data) {
        this.success = success;
        this.message = message;
        this.data = data;
        this.timestamp = Instant.now().toString();
    }
    
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(true, "成功", data);
    }
    
    public static <T> ApiResponse<T> success(String message, T data) {
        return new ApiResponse<>(true, message, data);
    }
    
    public static <T> ApiResponse<T> error(String message) {
        return new ApiResponse<>(false, message, null);
    }
    
    // getters and setters
}

// 统一异常处理
@ControllerAdvice
@RestController
public class GlobalExceptionHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
    @ExceptionHandler(EntityNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiResponse<Void> handleEntityNotFound(EntityNotFoundException ex) {
        logger.warn("实体未找到: {}", ex.getMessage());
        return ApiResponse.error(ex.getMessage());
    }
    
    @ExceptionHandler(ValidationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Void> handleValidationException(ValidationException ex) {
        logger.warn("验证失败: {}", ex.getMessage());
        return ApiResponse.error(ex.getMessage());
    }
    
    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public ApiResponse<Void> handleAccessDenied(AccessDeniedException ex) {
        logger.warn("访问被拒绝: {}", ex.getMessage());
        return ApiResponse.error("没有访问权限");
    }
    
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiResponse<Void> handleGenericException(Exception ex) {
        logger.error("服务器内部错误", ex);
        return ApiResponse.error("服务器内部错误,请稍后重试");
    }
    
    // 处理方法参数验证错误
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Map<String, String>> handleValidationErrors(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors()
                .forEach(error -> errors.put(error.getField(), error.getDefaultMessage()));
        
        return new ApiResponse<>(false, "参数验证失败", errors);
    }
}

// REST 控制器
@RestController
@RequestMapping("/api/v1/users")
@Validated
@Slf4j
public class UserController {
    
    private final UserService userService;
    
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    @GetMapping
    @PreAuthorize("hasRole('ADMIN')")
    public ApiResponse<Page<UserDto>> getUsers(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(required = false) String search) {
        
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<UserDto> users = userService.getUsers(pageable, search);
        
        return ApiResponse.success(users);
    }
    
    @GetMapping("/{id}")
    public ApiResponse<UserDto> getUser(@PathVariable Long id) {
        UserDto user = userService.getUserById(id);
        return ApiResponse.success(user);
    }
    
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public ApiResponse<UserDto> createUser(@Valid @RequestBody CreateUserRequest request) {
        UserDto createdUser = userService.createUser(request);
        return ApiResponse.success("用户创建成功", createdUser);
    }
    
    @PutMapping("/{id}")
    public ApiResponse<UserDto> updateUser(
            @PathVariable Long id, 
            @Valid @RequestBody UpdateUserRequest request) {
        
        UserDto updatedUser = userService.updateUser(id, request);
        return ApiResponse.success("用户更新成功", updatedUser);
    }
    
    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
    
    @GetMapping("/{id}/orders")
    public ApiResponse<List<OrderDto>> getUserOrders(@PathVariable Long id) {
        List<OrderDto> orders = userService.getUserOrders(id);
        return ApiResponse.success(orders);
    }
}

// DTO 类
public class CreateUserRequest {
    
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @NotBlank(message = "姓名不能为空")
    @Size(min = 2, max = 50, message = "姓名长度必须在2-50个字符之间")
    private String name;
    
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, message = "密码长度至少6位")
    @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).*$", 
             message = "密码必须包含大小写字母和数字")
    private String password;
    
    // getters and setters
}

// 服务层实现
@Service
@Transactional(readOnly = true)
@Slf4j
public class UserService {
    
    private final UserRepository userRepository;
    private final UserMapper userMapper;
    private final PasswordEncoder passwordEncoder;
    
    public UserService(UserRepository userRepository, UserMapper userMapper, 
                      PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.userMapper = userMapper;
        this.passwordEncoder = passwordEncoder;
    }
    
    public Page<UserDto> getUsers(Pageable pageable, String search) {
        Specification<User> spec = buildSearchSpecification(search);
        Page<User> users = userRepository.findAll(spec, pageable);
        return users.map(userMapper::toDto);
    }
    
    public UserDto getUserById(Long id) {
        User user = userRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("用户不存在: " + id));
        return userMapper.toDto(user);
    }
    
    @Transactional
    public UserDto createUser(CreateUserRequest request) {
        // 检查邮箱是否已存在
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new ValidationException("邮箱已存在: " + request.getEmail());
        }
        
        User user = new User();
        user.setEmail(request.getEmail());
        user.setName(request.getName());
        user.setPassword(passwordEncoder.encode(request.getPassword()));
        user.setStatus(UserStatus.ACTIVE);
        user.setCreatedAt(LocalDateTime.now());
        
        User savedUser = userRepository.save(user);
        log.info("创建用户成功: {}", savedUser.getEmail());
        
        return userMapper.toDto(savedUser);
    }
    
    @Transactional
    public UserDto updateUser(Long id, UpdateUserRequest request) {
        User user = userRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("用户不存在: " + id));
        
        user.setName(request.getName());
        user.setUpdatedAt(LocalDateTime.now());
        
        User updatedUser = userRepository.save(user);
        return userMapper.toDto(updatedUser);
    }
    
    @Transactional
    public void deleteUser(Long id) {
        User user = userRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("用户不存在: " + id));
        
        user.setStatus(UserStatus.DELETED);
        userRepository.save(user);
        log.info("删除用户: {}", id);
    }
    
    private Specification<User> buildSearchSpecification(String search) {
        return (root, query, criteriaBuilder) -> {
            if (StringUtils.isEmpty(search)) {
                return criteriaBuilder.conjunction();
            }
            
            String likePattern = "%" + search.toLowerCase() + "%";
            return criteriaBuilder.or(
                criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), likePattern),
                criteriaBuilder.like(criteriaBuilder.lower(root.get("email")), likePattern)
            );
        };
    }
}

第五部分:数据库与持久层技术

5.1 JPA 与 Hibernate

5.1.1 实体类映射
java 复制代码
// 基类实体
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @CreatedDate
    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
    
    @Version
    private Long version;
    
    // getters and setters
}

// 用户实体
@Entity
@Table(name = "users", 
       indexes = {
           @Index(name = "idx_user_email", columnList = "email", unique = true),
           @Index(name = "idx_user_status", columnList = "status")
       })
@NamedQueries({
    @NamedQuery(
        name = "User.findByStatus",
        query = "SELECT u FROM User u WHERE u.status = :status"
    )
})
public class User extends BaseEntity {
    
    @Column(name = "email", nullable = false, length = 100)
    private String email;
    
    @Column(name = "name", nullable = false, length = 50)
    private String name;
    
    @Column(name = "password_hash", nullable = false, length = 100)
    private String password;
    
    @Enumerated(EnumType.STRING)
    @Column(name = "status", length = 20)
    private UserStatus status = UserStatus.ACTIVE;
    
    @Column(name = "last_login_at")
    private LocalDateTime lastLoginAt;
    
    // 一对一关系
    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private UserProfile profile;
    
    // 一对多关系
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @OrderBy("createdAt DESC")
    private List<Order> orders = new ArrayList<>();
    
    // 多对多关系
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();
    
    // 构造函数
    public User() {}
    
    public User(String email, String name) {
        this.email = email;
        this.name = name;
    }
    
    // 业务方法
    public void addOrder(Order order) {
        orders.add(order);
        order.setUser(this);
    }
    
    public void removeOrder(Order order) {
        orders.remove(order);
        order.setUser(null);
    }
    
    public void addRole(Role role) {
        roles.add(role);
        role.getUsers().add(this);
    }
    
    // getters and setters
}

// 枚举定义
public enum UserStatus {
    ACTIVE, INACTIVE, SUSPENDED, DELETED
}

// 订单实体
@Entity
@Table(name = "orders")
public class Order extends BaseEntity {
    
    @Column(name = "order_number", unique = true, length = 20)
    private String orderNumber;
    
    @Column(name = "total_amount", precision = 10, scale = 2)
    private BigDecimal totalAmount;
    
    @Enumerated(EnumType.STRING)
    @Column(name = "status", length = 20)
    private OrderStatus status = OrderStatus.PENDING;
    
    // 多对一关系
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;
    
    // 一对多关系
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<OrderItem> items = new ArrayList<>();
    
    // 嵌入式对象
    @Embedded
    private Address shippingAddress;
    
    // 业务方法
    public void addItem(OrderItem item) {
        items.add(item);
        item.setOrder(this);
    }
    
    public BigDecimal calculateTotal() {
        return items.stream()
                .map(OrderItem::getSubtotal)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    
    // getters and setters
}

// 嵌入式地址类
@Embeddable
public class Address {
    
    @Column(name = "street", length = 100)
    private String street;
    
    @Column(name = "city", length = 50)
    private String city;
    
    @Column(name = "state", length = 50)
    private String state;
    
    @Column(name = "zip_code", length = 20)
    private String zipCode;
    
    @Column(name = "country", length = 50)
    private String country = "China";
    
    // getters and setters
}
5.1.2 Repository 数据访问层
java 复制代码
// 基础 Repository 接口
@NoRepositoryBean
public interface BaseRepository<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
    
    default Optional<T> findByIdOptional(ID id) {
        return findById(id);
    }
    
    List<T> findByCreatedAtBetween(LocalDateTime start, LocalDateTime end);
    
    @Query("SELECT COUNT(e) FROM #{#entityName} e")
    long countAll();
    
    @Modifying
    @Query("UPDATE #{#entityName} e SET e.status = :status WHERE e.id = :id")
    int updateStatus(@Param("id") ID id, @Param("status") String status);
}

// 用户 Repository
@Repository
public interface UserRepository extends BaseRepository<User, Long> {
    
    // 派生查询方法
    Optional<User> findByEmail(String email);
    
    boolean existsByEmail(String email);
    
    List<User> findByStatusOrderByCreatedAtDesc(UserStatus status);
    
    List<User> findByNameContainingIgnoreCase(String name);
    
    // @Query 注解查询
    @Query("SELECT u FROM User u WHERE u.email LIKE %:domain")
    List<User> findByEmailDomain(@Param("domain") String domain);
    
    @Query("SELECT u.name, COUNT(o) FROM User u LEFT JOIN u.orders o GROUP BY u.id")
    List<Object[]> findUserOrderCount();
    
    // 原生 SQL 查询
    @Query(value = "SELECT * FROM users WHERE DATE(created_at) = :date", nativeQuery = true)
    List<User> findByCreatedDate(@Param("date") LocalDate date);
    
    // 分页查询
    Page<User> findByStatus(UserStatus status, Pageable pageable);
    
    // 使用 Specification 进行复杂查询
    default Page<User> findActiveUsersWithOrders(Pageable pageable) {
        return findAll((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(criteriaBuilder.equal(root.get("status"), UserStatus.ACTIVE));
            predicates.add(criteriaBuilder.isNotEmpty(root.get("orders")));
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        }, pageable);
    }
}

// 自定义 Repository 实现
public interface UserRepositoryCustom {
    List<User> findComplexUsers(UserSearchCriteria criteria);
    Page<User> searchUsers(UserSearchCriteria criteria, Pageable pageable);
}

// 自定义 Repository 实现类
@Repository
@Transactional(readOnly = true)
public class UserRepositoryImpl implements UserRepositoryCustom {
    
    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public List<User> findComplexUsers(UserSearchCriteria criteria) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> root = query.from(User.class);
        
        List<Predicate> predicates = buildPredicates(criteria, cb, root);
        query.where(predicates.toArray(new Predicate[0]));
        
        TypedQuery<User> typedQuery = entityManager.createQuery(query);
        return typedQuery.getResultList();
    }
    
    @Override
    public Page<User> searchUsers(UserSearchCriteria criteria, Pageable pageable) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> root = query.from(User.class);
        
        List<Predicate> predicates = buildPredicates(criteria, cb, root);
        query.where(predicates.toArray(new Predicate[0]));
        
        // 排序
        List<Order> orders = new ArrayList<>();
        for (Sort.Order sortOrder : pageable.getSort()) {
            if (sortOrder.isAscending()) {
                orders.add(cb.asc(root.get(sortOrder.getProperty())));
            } else {
                orders.add(cb.desc(root.get(sortOrder.getProperty())));
            }
        }
        query.orderBy(orders);
        
        TypedQuery<User> typedQuery = entityManager.createQuery(query);
        typedQuery.setFirstResult((int) pageable.getOffset());
        typedQuery.setMaxResults(pageable.getPageSize());
        
        List<User> result = typedQuery.getResultList();
        
        // 获取总数
        Long total = getTotalCount(criteria);
        
        return new PageImpl<>(result, pageable, total);
    }
    
    private List<Predicate> buildPredicates(UserSearchCriteria criteria, 
                                           CriteriaBuilder cb, Root<User> root) {
        List<Predicate> predicates = new ArrayList<>();
        
        if (StringUtils.hasText(criteria.getKeyword())) {
            String likePattern = "%" + criteria.getKeyword().toLowerCase() + "%";
            predicates.add(cb.or(
                cb.like(cb.lower(root.get("name")), likePattern),
                cb.like(cb.lower(root.get("email")), likePattern)
            ));
        }
        
        if (criteria.getStatus() != null) {
            predicates.add(cb.equal(root.get("status"), criteria.getStatus()));
        }
        
        if (criteria.getStartDate() != null) {
            predicates.add(cb.greaterThanOrEqualTo(root.get("createdAt"), 
                                                 criteria.getStartDate()));
        }
        
        if (criteria.getEndDate() != null) {
            predicates.add(cb.lessThanOrEqualTo(root.get("createdAt"), 
                                              criteria.getEndDate()));
        }
        
        return predicates;
    }
    
    private Long getTotalCount(UserSearchCriteria criteria) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
        Root<User> root = countQuery.from(User.class);
        
        List<Predicate> predicates = buildPredicates(criteria, cb, root);
        countQuery.select(cb.count(root)).where(predicates.toArray(new Predicate[0]));
        
        return entityManager.createQuery(countQuery).getSingleResult();
    }
}

// 搜索条件类
@Data
public class UserSearchCriteria {
    private String keyword;
    private UserStatus status;
    private LocalDateTime startDate;
    private LocalDateTime endDate;
    private Boolean hasOrders;
}

5.2 数据库迁移与版本控制

5.2.1 Flyway 数据库迁移
sql 复制代码
-- V1__Create_users_table.sql
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(100) NOT NULL UNIQUE,
    name VARCHAR(50) NOT NULL,
    password_hash VARCHAR(100) NOT NULL,
    status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    version BIGINT DEFAULT 0
);

CREATE INDEX idx_user_email ON users(email);
CREATE INDEX idx_user_status ON users(status);

-- V2__Create_orders_table.sql
CREATE TABLE orders (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_number VARCHAR(20) UNIQUE NOT NULL,
    user_id BIGINT NOT NULL,
    total_amount DECIMAL(10,2),
    status VARCHAR(20) DEFAULT 'PENDING',
    shipping_street VARCHAR(100),
    shipping_city VARCHAR(50),
    shipping_state VARCHAR(50),
    shipping_zip_code VARCHAR(20),
    shipping_country VARCHAR(50) DEFAULT 'China',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    version BIGINT DEFAULT 0,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

CREATE INDEX idx_order_user_id ON orders(user_id);
CREATE INDEX idx_order_number ON orders(order_number);

-- V3__Create_order_items_table.sql
CREATE TABLE order_items (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_id BIGINT NOT NULL,
    product_name VARCHAR(100) NOT NULL,
    quantity INT NOT NULL,
    unit_price DECIMAL(10,2) NOT NULL,
    subtotal DECIMAL(10,2) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE
);

-- V4__Add_last_login_to_users.sql
ALTER TABLE users ADD COLUMN last_login_at TIMESTAMP NULL;

-- V5__Create_user_roles.sql
CREATE TABLE roles (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL UNIQUE,
    description VARCHAR(200),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE user_roles (
    user_id BIGINT NOT NULL,
    role_id BIGINT NOT NULL,
    assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE
);

-- V6__Insert_initial_data.sql
INSERT INTO roles (name, description) VALUES 
('ADMIN', '系统管理员'),
('USER', '普通用户'),
('MANAGER', '经理');

-- 回滚脚本 R__Drop_user_roles.sql
DROP TABLE IF EXISTS user_roles;
DROP TABLE IF EXISTS roles;
5.2.2 Flyway 配置
java 复制代码
@Configuration
public class FlywayConfig {
    
    @Bean
    public FlywayMigrationStrategy flywayMigrationStrategy() {
        return flyway -> {
            // 在迁移前执行的操作
            log.info("开始数据库迁移...");
            flyway.migrate();
        };
    }
    
    @Bean
    @ConfigurationProperties(prefix = "spring.flyway")
    public FlywayProperties flywayProperties() {
        return new FlywayProperties();
    }
}
yaml 复制代码
# application.yml 中的 Flyway 配置
spring:
  flyway:
    enabled: true
    locations: classpath:db/migration
    baseline-on-migrate: true
    baseline-version: 1
    validate-on-migrate: true
    out-of-order: false
    clean-disabled: true
    table: flyway_schema_history
    placeholders:
      table-prefix: ""

第六部分:高级主题与最佳实践

6.1 性能优化与缓存

6.1.1 Redis 缓存集成
java 复制代码
// 缓存配置
@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(30))
                .disableCachingNullValues()
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()));
        
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .withInitialCacheConfigurations(getCacheConfigurations())
                .transactionAware()
                .build();
    }
    
    private Map<String, RedisCacheConfiguration> getCacheConfigurations() {
        Map<String, RedisCacheConfiguration> cacheConfigs = new HashMap<>();
        
        cacheConfigs.put("users", RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer())));
        
        cacheConfigs.put("products", RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(15)));
        
        return cacheConfigs;
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 使用 Jackson 序列化
        Jackson2JsonRedisSerializer<Object> serializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), 
                                   ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        
        return template;
    }
}

// 缓存服务
@Service
@Slf4j
public class CacheService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private final ObjectMapper objectMapper;
    
    public CacheService(RedisTemplate<String, Object> redisTemplate, 
                       ObjectMapper objectMapper) {
        this.redisTemplate = redisTemplate;
        this.objectMapper = objectMapper;
    }
    
    public <T> void set(String key, T value, Duration ttl) {
        try {
            redisTemplate.opsForValue().set(key, value, ttl);
        } catch (Exception e) {
            log.error("Redis 设置缓存失败 key: {}", key, e);
        }
    }
    
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> type) {
        try {
            Object value = redisTemplate.opsForValue().get(key);
            if (value == null) {
                return null;
            }
            
            if (type.isInstance(value)) {
                return (T) value;
            }
            
            // 类型转换
            return objectMapper.convertValue(value, type);
        } catch (Exception e) {
            log.error("Redis 获取缓存失败 key: {}", key, e);
            return null;
        }
    }
    
    public boolean delete(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.delete(key));
        } catch (Exception e) {
            log.error("Redis 删除缓存失败 key: {}", key, e);
            return false;
        }
    }
    
    public boolean exists(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.error("Redis 检查键存在失败 key: {}", key, e);
            return false;
        }
    }
    
    public void setHash(String key, String hashKey, Object value) {
        try {
            redisTemplate.opsForHash().put(key, hashKey, value);
        } catch (Exception e) {
            log.error("Redis 设置哈希缓存失败 key: {}, hashKey: {}", key, hashKey, e);
        }
    }
    
    @SuppressWarnings("unchecked")
    public <T> T getHash(String key, String hashKey, Class<T> type) {
        try {
            Object value = redisTemplate.opsForHash().get(key, hashKey);
            if (value == null) {
                return null;
            }
            
            if (type.isInstance(value)) {
                return (T) value;
            }
            
            return objectMapper.convertValue(value, type);
        } catch (Exception e) {
            log.error("Redis 获取哈希缓存失败 key: {}, hashKey: {}", key, hashKey, e);
            return null;
        }
    }
}

// 使用缓存的服务
@Service
@Slf4j
public class CachedUserService {
    
    private final UserRepository userRepository;
    private final CacheService cacheService;
    private final UserMapper userMapper;
    
    private static final String USER_CACHE_PREFIX = "user:";
    private static final String USER_LIST_CACHE_KEY = "users:list";
    private static final Duration USER_CACHE_TTL = Duration.ofHours(1);
    
    public CachedUserService(UserRepository userRepository, CacheService cacheService,
                           UserMapper userMapper) {
        this.userRepository = userRepository;
        this.cacheService = cacheService;
        this.userMapper = userMapper;
    }
    
    @Cacheable(value = "users", key = "#id", unless = "#result == null")
    public UserDto getUserById(Long id) {
        log.debug("从数据库获取用户: {}", id);
        User user = userRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("用户不存在: " + id));
        return userMapper.toDto(user);
    }
    
    @Cacheable(value = "users", key = "#email", unless = "#result == null")
    public UserDto getUserByEmail(String email) {
        log.debug("从数据库获取用户: {}", email);
        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new EntityNotFoundException("用户不存在: " + email));
        return userMapper.toDto(user);
    }
    
    @Caching(
        put = {
            @CachePut(value = "users", key = "#result.id"),
            @CachePut(value = "users", key = "#result.email")
        },
        evict = {
            @CacheEvict(value = "users", key = "'list'"),
            @CacheEvict(value = "users", key = "'search:' + #result.status")
        }
    )
    public UserDto updateUser(UserDto userDto) {
        User user = userRepository.findById(userDto.getId())
                .orElseThrow(() -> new EntityNotFoundException("用户不存在: " + userDto.getId()));
        
        userMapper.updateUserFromDto(userDto, user);
        User updatedUser = userRepository.save(user);
        
        log.info("更新用户: {}", userDto.getId());
        return userMapper.toDto(updatedUser);
    }
    
    @CacheEvict(value = "users", allEntries = true)
    public void evictAllUserCaches() {
        log.info("清除所有用户缓存");
    }
    
    // 手动缓存管理
    public List<UserDto> getActiveUsers() {
        String cacheKey = "users:active";
        
        // 尝试从缓存获取
        List<UserDto> cachedUsers = cacheService.get(cacheKey, List.class);
        if (cachedUsers != null) {
            log.debug("从缓存获取活跃用户列表");
            return cachedUsers;
        }
        
        // 从数据库获取
        log.debug("从数据库获取活跃用户列表");
        List<User> users = userRepository.findByStatusOrderByCreatedAtDesc(UserStatus.ACTIVE);
        List<UserDto> userDtos = users.stream()
                .map(userMapper::toDto)
                .collect(Collectors.toList());
        
        // 设置缓存
        cacheService.set(cacheKey, userDtos, Duration.ofMinutes(30));
        
        return userDtos;
    }
}

6.2 安全与认证授权

6.2.1 Spring Security 配置
java 复制代码
// 安全配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig {
    
    private final UserDetailsService userDetailsService;
    private final JwtTokenProvider jwtTokenProvider;
    
    public SecurityConfig(UserDetailsService userDetailsService, 
                         JwtTokenProvider jwtTokenProvider) {
        this.userDetailsService = userDetailsService;
        this.jwtTokenProvider = jwtTokenProvider;
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .cors().and()
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .antMatchers("/api/public/**").permitAll()
            .antMatchers("/api/admin/**").hasRole("ADMIN")
            .antMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
            .antMatchers("/api/**").authenticated()
            .anyRequest().permitAll()
            .and()
            .apply(new JwtConfigurer(jwtTokenProvider));
        
        return http.build();
    }
    
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        
        config.setAllowCredentials(true);
        config.addAllowedOriginPattern("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.addExposedHeader("Authorization");
        
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

// JWT 配置
public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    
    private final JwtTokenProvider jwtTokenProvider;
    
    public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }
    
    @Override
    public void configure(HttpSecurity http) {
        JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider);
        http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

// JWT Token 过滤器
public class JwtTokenFilter extends OncePerRequestFilter {
    
    private final JwtTokenProvider jwtTokenProvider;
    
    public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain filterChain) throws ServletException, IOException {
        
        String token = resolveToken(request);
        
        if (token != null && jwtTokenProvider.validateToken(token)) {
            Authentication auth = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        
        filterChain.doFilter(request, response);
    }
    
    private String resolveToken(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

// JWT Token 提供者
@Component
public class JwtTokenProvider {
    
    @Value("${app.jwt.secret:defaultSecret}")
    private String jwtSecret;
    
    @Value("${app.jwt.expiration:86400000}")
    private long jwtExpirationMs;
    
    private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
    
    public String generateToken(Authentication authentication) {
        UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
        
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + jwtExpirationMs);
        
        return Jwts.builder()
                .setSubject(userPrincipal.getUsername())
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }
    
    public String getUsernameFromToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(token)
                .getBody();
        
        return claims.getSubject();
    }
    
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
            return true;
        } catch (SignatureException ex) {
            logger.error("Invalid JWT signature");
        } catch (MalformedJwtException ex) {
            logger.error("Invalid JWT token");
        } catch (ExpiredJwtException ex) {
            logger.error("Expired JWT token");
        } catch (UnsupportedJwtException ex) {
            logger.error("Unsupported JWT token");
        } catch (IllegalArgumentException ex) {
            logger.error("JWT claims string is empty");
        }
        return false;
    }
    
    public Authentication getAuthentication(String token) {
        String username = getUsernameFromToken(token);
        
        UserDetails userDetails = loadUserByUsername(username);
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }
    
    private UserDetails loadUserByUsername(String username) {
        // 实现用户加载逻辑
        return null; // 实际应该从数据库加载
    }
}

// 用户详情服务
@Service
@Transactional(readOnly = true)
public class CustomUserDetailsService implements UserDetailsService {
    
    private final UserRepository userRepository;
    
    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + email));
        
        return UserPrincipal.create(user);
    }
    
    public UserDetails loadUserById(Long id) {
        User user = userRepository.findById(id)
                .orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + id));
        
        return UserPrincipal.create(user);
    }
}

// 用户主体类
public class UserPrincipal implements UserDetails {
    
    private Long id;
    private String email;
    private String password;
    private Collection<? extends GrantedAuthority> authorities;
    
    public UserPrincipal(Long id, String email, String password, 
                        Collection<? extends GrantedAuthority> authorities) {
        this.id = id;
        this.email = email;
        this.password = password;
        this.authorities = authorities;
    }
    
    public static UserPrincipal create(User user) {
        List<GrantedAuthority> authorities = user.getRoles().stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
                .collect(Collectors.toList());
        
        return new UserPrincipal(
            user.getId(),
            user.getEmail(),
            user.getPassword(),
            authorities
        );
    }
    
    // UserDetails 接口方法实现
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }
    
    @Override
    public String getPassword() {
        return password;
    }
    
    @Override
    public String getUsername() {
        return email;
    }
    
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    
    @Override
    public boolean isEnabled() {
        return true;
    }
    
    // getters
    public Long getId() {
        return id;
    }
}

6.3 微服务与分布式系统

6.3.1 Spring Cloud 微服务配置
java 复制代码
// 服务注册与发现
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class UserServiceApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

// 配置类
@Configuration
public class FeignConfig {
    
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
    
    @Bean
    public ErrorDecoder errorDecoder() {
        return new CustomErrorDecoder();
    }
}

// Feign 客户端
@FeignClient(name = "order-service", path = "/api/orders")
public interface OrderServiceClient {
    
    @GetMapping("/user/{userId}")
    List<OrderDto> getUserOrders(@PathVariable("userId") Long userId);
    
    @PostMapping
    OrderDto createOrder(@RequestBody CreateOrderRequest request);
    
    @PutMapping("/{orderId}/status")
    OrderDto updateOrderStatus(@PathVariable("orderId") Long orderId, 
                              @RequestBody UpdateOrderStatusRequest request);
}

// 服务调用示例
@Service
@Slf4j
public class UserOrderService {
    
    private final OrderServiceClient orderServiceClient;
    private final CircuitBreakerFactory circuitBreakerFactory;
    
    public UserOrderService(OrderServiceClient orderServiceClient,
                           CircuitBreakerFactory circuitBreakerFactory) {
        this.orderServiceClient = orderServiceClient;
        this.circuitBreakerFactory = circuitBreakerFactory;
    }
    
    public List<OrderDto> getUserOrdersWithFallback(Long userId) {
        CircuitBreaker circuitBreaker = circuitBreakerFactory.create("order-service");
        
        return circuitBreaker.run(
            () -> orderServiceClient.getUserOrders(userId),
            throwable -> {
                log.warn("订单服务调用失败,使用降级策略", throwable);
                return Collections.emptyList();
            }
        );
    }
    
    @Retry(name = "order-service", fallbackMethod = "createOrderFallback")
    public OrderDto createOrderWithRetry(CreateOrderRequest request) {
        return orderServiceClient.createOrder(request);
    }
    
    public OrderDto createOrderFallback(CreateOrderRequest request, Exception ex) {
        log.error("创建订单重试失败", ex);
        // 返回默认值或抛出业务异常
        throw new BusinessException("订单服务暂时不可用");
    }
}

// 配置中心配置
@Configuration
@RefreshScope
public class AppConfig {
    
    @Value("${app.feature.enabled:false}")
    private boolean featureEnabled;
    
    @Value("${app.cache.ttl:300}")
    private long cacheTtl;
    
    @Bean
    @RefreshScope
    public CacheManager cacheManager() {
        // 动态配置的缓存管理器
        return new ConcurrentMapCacheManager();
    }
    
    // getters
}

第七部分:部署与运维

7.1 Docker 容器化部署

7.1.1 Dockerfile 配置
dockerfile 复制代码
# 多阶段构建 Dockerfile
FROM openjdk:11-jdk-slim as builder

# 安装 Maven
RUN apt-get update && apt-get install -y maven

WORKDIR /app
COPY pom.xml .
COPY src ./src

# 构建应用
RUN mvn clean package -DskipTests

# 运行时镜像
FROM openjdk:11-jre-slim

# 安装必要的工具
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    && rm -rf /var/lib/apt/lists/*

# 创建应用用户
RUN groupadd -r appuser && useradd -r -g appuser appuser

# 创建应用目录
RUN mkdir -p /app/logs && chown -R appuser:appuser /app

USER appuser
WORKDIR /app

# 复制构建结果
COPY --from=builder /app/target/*.jar app.jar

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8080/actuator/health || exit 1

# 环境变量
ENV JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC -Djava.security.egd=file:/dev/./urandom"
ENV SPRING_PROFILES_ACTIVE="docker"

# 暴露端口
EXPOSE 8080

# 启动应用
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
7.1.2 Docker Compose 配置
yaml 复制代码
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=docker
      - DB_HOST=mysql
      - REDIS_HOST=redis
    depends_on:
      - mysql
      - redis
    networks:
      - app-network
    deploy:
      resources:
        limits:
          memory: 1G
        reservations:
          memory: 512M

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: myapp
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppassword
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
      - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - app-network
    command: 
      - --default-authentication-plugin=mysql_native_password
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci

  redis:
    image: redis:6.2-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    networks:
      - app-network
    command: redis-server --appendonly yes

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/ssl:/etc/nginx/ssl
    depends_on:
      - app
    networks:
      - app-network

volumes:
  mysql-data:
  redis-data:

networks:
  app-network:
    driver: bridge

7.2 监控与日志

7.2.1 Spring Boot Actuator 配置
yaml 复制代码
# 监控配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,loggers,prometheus
      base-path: /actuator
  endpoint:
    health:
      show-details: always
      show-components: always
    metrics:
      enabled: true
    loggers:
      enabled: true
  metrics:
    export:
      prometheus:
        enabled: true
    distribution:
      percentiles-histogram:
        http.server.requests: true
  info:
    env:
      enabled: true
7.2.2 自定义健康检查
java 复制代码
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
    
    private final DataSource dataSource;
    
    public DatabaseHealthIndicator(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    @Override
    public Health health() {
        try (Connection connection = dataSource.getConnection()) {
            // 执行简单的数据库检查
            try (Statement statement = connection.createStatement()) {
                statement.execute("SELECT 1");
            }
            
            return Health.up()
                    .withDetail("database", "MySQL")
                    .withDetail("validationQuery", "SELECT 1")
                    .build();
                    
        } catch (Exception e) {
            return Health.down(e)
                    .withDetail("database", "MySQL")
                    .withDetail("error", e.getMessage())
                    .build();
        }
    }
}

@Component
public class RedisHealthIndicator implements HealthIndicator {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public RedisHealthIndicator(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    @Override
    public Health health() {
        try {
            // 执行简单的 Redis 操作
            String testKey = "health:check";
            redisTemplate.opsForValue().set(testKey, "test", Duration.ofSeconds(10));
            String value = (String) redisTemplate.opsForValue().get(testKey);
            
            if ("test".equals(value)) {
                return Health.up()
                        .withDetail("redis", "connected")
                        .build();
            } else {
                return Health.down()
                        .withDetail("redis", "unexpected response")
                        .build();
            }
        } catch (Exception e) {
            return Health.down(e)
                    .withDetail("redis", "connection failed")
                    .withDetail("error", e.getMessage())
                    .build();
        }
    }
}

第八部分:学习路线与持续提升

8.1 分阶段学习计划

阶段一:基础入门(1-2个月)
  1. Java 语言基础
    • 语法、面向对象、集合框架
    • 异常处理、IO 操作、多线程
  2. 开发工具掌握
    • IntelliJ IDEA 使用
    • Maven/Gradle 构建工具
    • Git 版本控制
阶段二:Spring 框架(2-3个月)
  1. Spring Core
    • IOC、DI、AOP 概念
    • Bean 生命周期管理
  2. Spring Boot
    • 自动配置原理
    • Starter 使用
    • 应用配置管理
  3. 数据访问
    • JPA/Hibernate
    • 事务管理
    • 数据库迁移
阶段三:高级特性(2-3个月)
  1. 微服务架构
    • Spring Cloud 组件
    • 服务注册发现
    • 配置中心
  2. 性能优化
    • 缓存策略
    • 数据库优化
    • JVM 调优
  3. 安全防护
    • Spring Security
    • OAuth2/JWT
    • 安全最佳实践
阶段四:生产实践(持续)
  1. 容器化部署
    • Docker/Kubernetes
    • CI/CD 流水线
  2. 监控运维
    • 应用监控
    • 日志分析
    • 性能调优
  3. 架构设计
    • 系统架构模式
    • 领域驱动设计
    • 代码重构

8.2 持续学习资源

官方文档
在线课程
  • Spring 官方教程
  • Baeldung Java/Spring 教程
  • Java Brains YouTube 频道
实践项目
  • 个人博客系统
  • 电商平台
  • 微服务示例项目
  • 开源项目贡献

8.3 社区参与

技术社区
  • Stack Overflow
  • GitHub
  • 掘金、CSDN
  • 公司内部技术分享
持续提升建议
  1. 代码审查:参与团队代码审查,学习优秀代码风格
  2. 技术分享:定期进行技术分享,巩固知识
  3. 开源贡献:参与开源项目,了解最佳实践
  4. 技术博客:撰写技术博客,加深理解
  5. 参加技术会议:了解行业最新动态

总结

从 .NET 转型到 Java 是一个系统性的过程,需要从语言基础开始,逐步深入到框架使用、系统架构和运维部署。关键成功因素包括:

  1. 扎实的基础:深入理解 Java 语言特性和 JVM 原理
  2. 框架熟练度:掌握 Spring 生态系统的核心组件
  3. 工程化能力:熟悉构建工具、测试框架和 CI/CD
  4. 架构思维:理解微服务、分布式系统设计原则
  5. 持续学习:跟上技术发展趋势,不断更新知识体系

通过系统的学习和实践,.NET 开发者可以成功转型为 Java 开发者,并在新的技术栈中发挥更大的价值。记住,转型不仅是技术栈的切换,更是思维方式和开发理念的更新。保持开放的心态和持续学习的态度,是成功转型的关键。

相关推荐
z晨晨2 小时前
互联网大厂Java求职面试场景
java·redis·spring·面试·多线程·互联网大厂
i小杨3 小时前
前端埋点(打点)方案
前端·状态模式
灿烂阳光g3 小时前
主线程的Looper消息循环是如何建立的
android
方圆想当图灵3 小时前
深入浅出 gRPC
java·后端·github
卷Java3 小时前
uni-app 模板语法修复说明
java·数据库·spring boot·uni-app·mybatis
前端加油站3 小时前
时间转换那些事
前端·javascript
渣哥3 小时前
新手最容易忽略!Spring 自动装配的 5 种方式全解析
java
ASKED_20193 小时前
ChatGPT From Zero To Hero - LLM学习笔记(一)
笔记·学习·chatgpt
GoldenaArcher3 小时前
Postman 学习笔记 III:CLI 自动化测试与 Jenkins CI/CD 实践
笔记·学习·postman