第二阶段:Java进阶技能④常用工具与框架入门

🚀 第二阶段:Java进阶技能④常用工具与框架入门

💡 学习目标:掌握Java开发中常用的工具和框架,包括构建工具、测试框架、日志框架和Web开发基础,为项目实战阶段打下坚实基础

🔨 构建工具详解

🎯 构建工具概述

🌟 构建工具:自动化项目构建、依赖管理、测试执行和部署的工具,是现代Java开发的基础设施

✨ 构建工具的核心价值
🔨 构建工具功能
│
├── 📦 依赖管理
│   ├── 自动下载依赖库
│   ├── 版本冲突解决
│   └── 传递依赖处理
│
├── 🏗️ 项目构建
│   ├── 源码编译
│   ├── 资源处理
│   └── 打包部署
│
├── 🧪 测试集成
│   ├── 单元测试执行
│   ├── 集成测试支持
│   └── 测试报告生成
│
└── 🚀 生命周期管理
    ├── 标准化构建流程
    ├── 插件扩展机制
    └── 多环境配置

🚀 构建工具安装与配置

📦 Maven 安装配置

🎯 Windows 系统安装 Maven

# 方法一:使用 Chocolatey 包管理器(推荐)
# 1. 安装 Chocolatey(如果未安装)
# 在管理员权限的 PowerShell 中执行:
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

# 2. 使用 Chocolatey 安装 Maven
choco install maven

# 方法二:手动安装
# 1. 下载 Maven
# 访问 https://maven.apache.org/download.cgi
# 下载 apache-maven-3.9.x-bin.zip

# 2. 解压到指定目录
# 例如:C:\Program Files\Apache\maven

# 3. 配置环境变量
# 系统变量:
# MAVEN_HOME = C:\Program Files\Apache\maven
# 在 Path 中添加:%MAVEN_HOME%\bin

# 4. 验证安装
mvn --version

🎯 macOS 系统安装 Maven

# 方法一:使用 Homebrew(推荐)
# 1. 安装 Homebrew(如果未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 2. 使用 Homebrew 安装 Maven
brew install maven

# 方法二:手动安装
# 1. 下载 Maven
curl -O https://archive.apache.org/dist/maven/maven-3/3.9.4/binaries/apache-maven-3.9.4-bin.tar.gz

# 2. 解压
tar -xzf apache-maven-3.9.4-bin.tar.gz
sudo mv apache-maven-3.9.4 /opt/maven

# 3. 配置环境变量(添加到 ~/.bash_profile 或 ~/.zshrc)
export MAVEN_HOME=/opt/maven
export PATH=$MAVEN_HOME/bin:$PATH

# 4. 重新加载配置
source ~/.bash_profile  # 或 source ~/.zshrc

# 5. 验证安装
mvn --version
⚙️ Gradle 安装配置

🎯 Windows 系统安装 Gradle

# 方法一:使用 Chocolatey(推荐)
choco install gradle

# 方法二:使用 Scoop
# 1. 安装 Scoop(如果未安装)
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
irm get.scoop.sh | iex

# 2. 安装 Gradle
scoop install gradle

# 方法三:手动安装
# 1. 下载 Gradle
# 访问 https://gradle.org/releases/
# 下载 gradle-8.x-bin.zip

# 2. 解压到指定目录
# 例如:C:\Program Files\Gradle\gradle-8.4

# 3. 配置环境变量
# 系统变量:
# GRADLE_HOME = C:\Program Files\Gradle\gradle-8.4
# 在 Path 中添加:%GRADLE_HOME%\bin

# 4. 验证安装
gradle --version

🎯 macOS 系统安装 Gradle

# 方法一:使用 Homebrew(推荐)
brew install gradle

# 方法二:使用 SDKMAN!(推荐用于管理多版本)
# 1. 安装 SDKMAN!
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"

# 2. 安装 Gradle
sdk install gradle

# 3. 查看可用版本
sdk list gradle

# 4. 安装特定版本
sdk install gradle 8.4

# 方法三:手动安装
# 1. 下载 Gradle
curl -L https://services.gradle.org/distributions/gradle-8.4-bin.zip -o gradle-8.4-bin.zip

# 2. 解压
unzip gradle-8.4-bin.zip
sudo mv gradle-8.4 /opt/gradle

# 3. 配置环境变量(添加到 ~/.bash_profile 或 ~/.zshrc)
export GRADLE_HOME=/opt/gradle
export PATH=$GRADLE_HOME/bin:$PATH

# 4. 重新加载配置
source ~/.bash_profile  # 或 source ~/.zshrc

# 5. 验证安装
gradle --version
🔧 构建工具基本使用

🎯 Maven 基本使用

# 1. 创建新项目
mvn archetype:generate \
    -DgroupId=com.example \
    -DartifactId=my-first-app \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

# 2. 进入项目目录
cd my-first-app

# 3. 编译项目
mvn compile

# 4. 运行测试
mvn test

# 5. 打包项目
mvn package

# 6. 清理项目
mvn clean

# 7. 完整构建流程
mvn clean compile test package

# 8. 安装到本地仓库
mvn install

# 9. 查看项目信息
mvn help:describe -Dplugin=compiler
mvn dependency:tree

🎯 Gradle 基本使用

# 1. 初始化新项目
gradle init

# 选择项目类型:
# 1: basic
# 2: application
# 3: library
# 4: Gradle plugin

# 选择语言:
# 1: C++
# 2: Groovy
# 3: Java
# 4: Kotlin
# 5: Scala
# 6: Swift

# 2. 或者创建 Java 应用项目
gradle init --type java-application

# 3. 编译项目
gradle compileJava

# 4. 运行测试
gradle test

# 5. 构建项目
gradle build

# 6. 清理项目
gradle clean

# 7. 运行应用(如果是 application 项目)
gradle run

# 8. 查看任务列表
gradle tasks

# 9. 查看项目依赖
gradle dependencies

# 10. 生成 Wrapper(推荐)
gradle wrapper

🎯 Gradle Wrapper 使用(推荐)

# Gradle Wrapper 的优势:
# 1. 确保团队使用相同的 Gradle 版本
# 2. 无需预先安装 Gradle
# 3. 自动下载指定版本的 Gradle

# 生成 Wrapper
gradle wrapper --gradle-version 8.4

# 使用 Wrapper 执行任务(推荐方式)
# Windows:
gradlew.bat build
gradlew.bat test
gradlew.bat run

# macOS/Linux:
./gradlew build
./gradlew test
./gradlew run

# 查看 Wrapper 版本
./gradlew --version
🛠️ IDE 集成配置

🎯 IntelliJ IDEA 配置

Maven 配置:
1. File → Settings → Build, Execution, Deployment → Build Tools → Maven
2. 设置 Maven home directory(如果使用自定义安装)
3. 设置 User settings file(可选,自定义 settings.xml)
4. 设置 Local repository(可选,自定义本地仓库位置)

Gradle 配置:
1. File → Settings → Build, Execution, Deployment → Build Tools → Gradle
2. 选择 Gradle JVM
3. 选择 Use Gradle from(推荐选择 'gradle-wrapper.properties' file)
4. 配置 Build and run using: Gradle(推荐)

🎯 VS Code 配置

推荐插件:
1. Extension Pack for Java(包含 Maven 和 Gradle 支持)
2. Gradle for Java
3. Maven for Java

配置文件 settings.json:
{
    "java.configuration.maven.userSettings": "path/to/settings.xml",
    "java.gradle.buildServer.enabled": "on"
}
✅ 安装验证示例

🎯 快速验证安装

# 验证 Maven 安装
mvn --version
# 期望输出类似:
# Apache Maven 3.9.4 (dfbb324ad4a7c8fb0bf182e6d91b0ae20e3d2dd9)
# Maven home: /opt/maven
# Java version: 17.0.8, vendor: Eclipse Adoptium

# 验证 Gradle 安装
gradle --version
# 期望输出类似:
# Gradle 8.4
# Build time:   2023-10-04 20:52:13 UTC
# Revision:     e9251e572c9bd1d01e503a0dfdf43aedaecdc4c
# Kotlin:       1.9.10
# Groovy:       3.0.17
# Ant:          Apache Ant(TM) version 1.10.13 compiled on January 4 2023
# JVM:          17.0.8 (Eclipse Adoptium 17.0.8+7)
# OS:           Mac OS X 13.6 x86_64

# 创建第一个 Maven 项目进行测试
mvn archetype:generate \
    -DgroupId=com.example.test \
    -DartifactId=maven-test \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

cd maven-test
mvn clean compile test

# 创建第一个 Gradle 项目进行测试
mkdir gradle-test
cd gradle-test
gradle init --type java-application --dsl groovy --test-framework junit-jupiter
./gradlew build

🎯 常见问题解决

# 问题1:Maven 命令找不到
# 解决:检查环境变量配置
echo $MAVEN_HOME    # macOS/Linux
echo %MAVEN_HOME%   # Windows

# 问题2:Gradle 下载慢
# 解决:配置国内镜像(已在前面的优化指南中说明)

# 问题3:Java 版本不兼容
# 解决:检查 Java 版本
java --version
# 确保使用 Java 8 或更高版本

# 问题4:权限问题(macOS/Linux)
# 解决:使用 sudo 或修改目录权限
sudo chown -R $(whoami) /opt/maven
sudo chown -R $(whoami) /opt/gradle
🚀 5分钟快速上手

🎯 Maven 快速上手

# 1. 创建项目(30秒)
mvn archetype:generate \
    -DgroupId=com.example.quickstart \
    -DartifactId=my-first-maven-app \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

# 2. 进入项目目录
cd my-first-maven-app

# 3. 查看项目结构
tree .
# my-first-maven-app/
# ├── pom.xml
# └── src/
#     ├── main/java/com/example/quickstart/App.java
#     └── test/java/com/example/quickstart/AppTest.java

# 4. 编译并运行测试(1分钟)
mvn clean compile test

# 5. 打包应用
mvn package

# 6. 运行应用
java -cp target/classes com.example.quickstart.App

🎯 Gradle 快速上手

# 1. 创建项目(30秒)
mkdir my-first-gradle-app
cd my-first-gradle-app
gradle init --type java-application --dsl groovy

# 2. 查看项目结构
tree .
# my-first-gradle-app/
# ├── build.gradle
# ├── gradle/wrapper/
# ├── gradlew
# ├── gradlew.bat
# ├── settings.gradle
# └── app/src/
#     ├── main/java/my/first/gradle/app/App.java
#     └── test/java/my/first/gradle/app/AppTest.java

# 3. 编译并运行测试(1分钟)
./gradlew build

# 4. 运行应用
./gradlew run

# 5. 查看可用任务
./gradlew tasks

🎯 第一次修改代码

// 修改 App.java 文件
public class App {
    public String getGreeting() {
        return "Hello World!";
    }

    public static void main(String[] args) {
        System.out.println(new App().getGreeting());
        System.out.println("我的第一个Java项目!");
    }
}

// Maven: 重新构建和运行
mvn clean compile exec:java -Dexec.mainClass="com.example.quickstart.App"

// Gradle: 重新构建和运行
./gradlew run

💻 Maven详解

🎯 Maven基础配置

🎯 示例1:Maven项目结构和配置

<!-- pom.xml - Maven项目配置文件,Maven项目的核心配置文件 -->
<?xml version="1.0" encoding="UTF-8"?>
<!--
    project标签:Maven项目的根元素
    xmlns:定义XML命名空间,确保标签的唯一性
    xsi:schemaLocation:指定XML Schema位置,用于验证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:POM模型版本,目前固定为4.0.0 -->
    <modelVersion>4.0.0</modelVersion>

    <!-- groupId:项目组织的唯一标识,通常使用反向域名 -->
    <groupId>com.example</groupId>

    <!-- artifactId:项目的唯一标识名称,通常是项目名 -->
    <artifactId>my-java-app</artifactId>

    <!-- version:项目版本号,遵循语义化版本规范 -->
    <version>1.0.0</version>

    <!-- packaging:打包方式,jar=普通Java项目,war=Web项目,pom=父项目 -->
    <packaging>jar</packaging>

    <!-- 项目描述信息:可选但建议填写,便于项目管理 -->
    <name>My Java Application</name>
    <description>一个示例Java应用程序</description>
    <url>https://github.com/example/my-java-app</url>

    <!--
        属性配置:定义项目中使用的变量,便于统一管理和版本控制
        使用${属性名}的方式在其他地方引用
    -->
    <properties>
        <!-- Java编译器源码版本:指定源代码使用的Java版本 -->
        <maven.compiler.source>17</maven.compiler.source>

        <!-- Java编译器目标版本:指定编译后字节码的Java版本 -->
        <maven.compiler.target>17</maven.compiler.target>

        <!-- 项目源码编码:确保在不同操作系统上编码一致 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!--
            依赖版本管理:统一管理依赖库版本,避免版本冲突
            好处:1.统一升级 2.避免冲突 3.便于维护
        -->
        <junit.version>5.9.2</junit.version>        <!-- JUnit测试框架版本 -->
        <slf4j.version>2.0.7</slf4j.version>        <!-- SLF4J日志门面版本 -->
        <jackson.version>2.15.2</jackson.version>   <!-- Jackson JSON处理库版本 -->
    </properties>

    <!--
        依赖管理:声明项目所需的外部库
        Maven会自动下载这些依赖及其传递依赖到本地仓库
    -->
    <dependencies>
        <!--
            核心依赖:项目运行时必需的库
            这些依赖会被打包到最终的jar文件中
        -->

        <!-- SLF4J日志门面:提供统一的日志接口 -->
        <dependency>
            <groupId>org.slf4j</groupId>                    <!-- 组织标识 -->
            <artifactId>slf4j-api</artifactId>              <!-- 项目标识 -->
            <version>${slf4j.version}</version>             <!-- 版本号,引用properties中定义的变量 -->
            <!-- scope默认为compile,表示编译、测试、运行时都需要 -->
        </dependency>

        <!-- Logback日志实现:SLF4J的具体实现,负责实际的日志输出 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.8</version>
            <!--
                logback-classic包含了logback-core和slf4j-api的依赖
                Maven会自动处理这些传递依赖
            -->
        </dependency>

        <!--
            JSON处理库:用于JSON数据的序列化和反序列化
            jackson-databind包含了jackson-core和jackson-annotations
        -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <!--
            测试依赖:仅在测试阶段使用的库
            scope=test表示只在测试编译和测试运行时需要,不会打包到最终jar中
        -->

        <!-- JUnit 5测试框架:现代化的Java测试框架 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>          <!-- 包含了junit-jupiter-api和junit-jupiter-engine -->
            <version>${junit.version}</version>
            <scope>test</scope>                             <!-- 测试作用域:仅测试时使用 -->
        </dependency>

        <!-- Mockito模拟框架:用于创建模拟对象进行单元测试 -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>5.3.1</version>
            <scope>test</scope>                             <!-- 测试作用域 -->
            <!--
                Mockito常用作用域说明:
                - compile: 编译、测试、运行时都需要(默认)
                - test: 仅测试时需要
                - provided: 编译和测试时需要,运行时由容器提供
                - runtime: 运行和测试时需要,编译时不需要
            -->
        </dependency>
    </dependencies>

    <!--
        构建配置:定义项目的构建过程和插件
        Maven通过插件来执行各种构建任务
    -->
    <build>
        <plugins>
            <!--
                编译插件:控制Java源码的编译过程
                Maven核心插件,负责将.java文件编译为.class文件
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>                   <!-- 插件版本,建议使用最新稳定版 -->
                <configuration>
                    <!-- 源码Java版本:指定源代码使用的Java语言版本 -->
                    <source>17</source>
                    <!-- 目标Java版本:指定编译后字节码兼容的Java版本 -->
                    <target>17</target>
                    <!-- 编译时字符编码:确保中文等特殊字符正确处理 -->
                    <encoding>UTF-8</encoding>
                    <!--
                        其他可选配置:
                        <compilerArgs>: 传递给编译器的额外参数
                        <showWarnings>: 是否显示编译警告
                        <showDeprecation>: 是否显示过时API警告
                    -->
                </configuration>
            </plugin>

            <!--
                测试插件:控制单元测试的执行
                Surefire插件负责运行src/test/java下的测试类
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.1.2</version>
                <configuration>
                    <!--
                        测试类包含模式:指定哪些类会被当作测试类执行
                        **表示任意层级目录,*表示任意字符
                    -->
                    <includes>
                        <include>**/*Test.java</include>      <!-- 以Test结尾的类 -->
                        <include>**/*Tests.java</include>     <!-- 以Tests结尾的类 -->
                        <!--
                            默认模式还包括:
                            **/Test*.java (以Test开头)
                            **/*TestCase.java (以TestCase结尾)
                        -->
                    </includes>
                    <!--
                        其他常用配置:
                        <excludes>: 排除某些测试类
                        <parallel>: 并行执行测试
                        <threadCount>: 并行线程数
                        <skipTests>: 跳过测试执行
                    -->
                </configuration>
            </plugin>

            <!--
                JAR打包插件:控制JAR文件的生成
                负责将编译后的class文件和资源文件打包成jar
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.3.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <!--
                                主类配置:指定jar文件的入口点
                                使用java -jar命令时会执行这个类的main方法
                            -->
                            <mainClass>com.example.Application</mainClass>
                            <!--
                                其他manifest配置:
                                <addClasspath>true</addClasspath>: 添加classpath到manifest
                                <classpathPrefix>lib/</classpathPrefix>: classpath前缀
                            -->
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <!--
                Shade插件:创建包含所有依赖的可执行JAR(Fat JAR/Uber JAR)
                解决依赖问题,生成可以直接运行的jar文件
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.5.0</version>
                <executions>
                    <execution>
                        <!-- 绑定到package阶段:在mvn package时自动执行 -->
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>                 <!-- 执行shade目标 -->
                        </goals>
                        <configuration>
                            <transformers>
                                <!--
                                    Manifest转换器:修改jar的MANIFEST.MF文件
                                    设置Main-Class属性,使jar可以直接执行
                                -->
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.example.Application</mainClass>
                                </transformer>
                                <!--
                                    其他常用转换器:
                                    ServicesResourceTransformer: 合并META-INF/services文件
                                    AppendingTransformer: 追加内容到文件
                                    XmlAppendingTransformer: 合并XML文件
                                -->
                            </transformers>
                            <!--
                                其他配置选项:
                                <minimizeJar>true</minimizeJar>: 最小化jar,移除未使用的类
                                <filters>: 过滤器,控制哪些文件被包含
                                <relocations>: 重定位包名,避免依赖冲突
                            -->
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <!--
        多环境配置(Profiles):根据不同环境使用不同的配置
        通过mvn clean package -P环境名 来激活特定环境
    -->
    <profiles>
        <!-- 开发环境配置 -->
        <profile>
            <id>dev</id>                                    <!-- 环境标识符 -->
            <activation>
                <activeByDefault>true</activeByDefault>     <!-- 默认激活的环境 -->
                <!--
                    其他激活条件:
                    <property>: 根据系统属性激活
                    <jdk>: 根据JDK版本激活
                    <os>: 根据操作系统激活
                    <file>: 根据文件存在性激活
                -->
            </activation>
            <properties>
                <env>development</env>                      <!-- 环境变量,可在其他地方引用 -->
                <!-- 开发环境特有的属性 -->
                <database.url>jdbc:h2:mem:devdb</database.url>
                <log.level>DEBUG</log.level>
            </properties>
        </profile>

        <!-- 生产环境配置 -->
        <profile>
            <id>prod</id>
            <properties>
                <env>production</env>
                <!-- 生产环境特有的属性 -->
                <database.url>jdbc:mysql://prod-server:3306/proddb</database.url>
                <log.level>WARN</log.level>
            </properties>
            <!--
                生产环境可以有不同的依赖、插件配置等
                <dependencies>: 环境特定的依赖
                <build>: 环境特定的构建配置
            -->
        </profile>
    </profiles>
</project>
🔧 Maven实用命令

🎯 示例2:Maven常用命令和最佳实践

# Maven项目生命周期命令

# 1. 项目创建
mvn archetype:generate \
    -DgroupId=com.example \
    -DartifactId=my-app \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

# 2. 编译相关
mvn compile                 # 编译主代码
mvn test-compile           # 编译测试代码
mvn clean compile          # 清理后编译

# 3. 测试相关
mvn test                   # 运行测试
mvn test -Dtest=UserTest   # 运行指定测试类
mvn test -Dtest=*Service*  # 运行匹配的测试类

# 4. 打包部署
mvn package               # 打包项目
mvn install              # 安装到本地仓库
mvn deploy               # 部署到远程仓库

# 5. 依赖管理
mvn dependency:tree       # 查看依赖树
mvn dependency:analyze    # 分析依赖
mvn dependency:resolve    # 解析依赖

# 6. 项目信息
mvn help:effective-pom    # 查看有效POM
mvn versions:display-dependency-updates  # 检查依赖更新

# 7. 多模块项目
mvn clean install -pl module1,module2    # 构建指定模块
mvn clean install -am -pl web-module     # 构建模块及其依赖

# 8. 跳过测试
mvn clean package -DskipTests            # 跳过测试执行
mvn clean package -Dmaven.test.skip=true # 跳过测试编译和执行

🎯 Gradle入门

💻 Gradle配置示例

🎯 示例3:Gradle构建脚本

// build.gradle - Gradle构建脚本,使用Groovy DSL语法
// Gradle是基于Groovy的构建工具,语法更简洁灵活

// 插件配置:声明项目使用的Gradle插件
plugins {
    id 'java'                                           // Java插件:提供Java项目的基本构建能力
    id 'application'                                    // 应用插件:支持运行Java应用程序
    id 'org.springframework.boot' version '3.1.0'      // Spring Boot插件:简化Spring Boot项目构建
    id 'io.spring.dependency-management' version '1.1.0' // 依赖管理插件:管理Spring相关依赖版本
}

// 项目基本信息:类似Maven的groupId、artifactId、version
group = 'com.example'           // 项目组织标识,对应Maven的groupId
version = '1.0.0'               // 项目版本号
sourceCompatibility = '17'     // Java源码兼容性版本

//
// 仓库配置:指定依赖库的下载源
// Gradle会按顺序在这些仓库中查找依赖
//
repositories {
    mavenCentral()              // Maven中央仓库:最常用的Java库仓库
    maven {
        // 自定义Maven仓库:Spring的里程碑版本仓库
        url 'https://repo.spring.io/milestone'
        // 可以添加认证信息:
        // credentials {
        //     username = 'user'
        //     password = 'password'
        // }
    }
    // 其他常用仓库:
    // google()                 // Google仓库,主要用于Android
    // gradlePluginPortal()     // Gradle插件仓库
    // mavenLocal()             // 本地Maven仓库
}

//
// 依赖配置:声明项目所需的外部库
// Gradle使用配置名称来区分不同类型的依赖
//
dependencies {
    //
    // implementation:编译和运行时依赖,但不会传递给依赖此项目的其他项目
    // 相当于Maven的compile scope,但提供更好的依赖隔离
    //
    implementation 'org.springframework.boot:spring-boot-starter'        // Spring Boot核心启动器
    implementation 'org.springframework.boot:spring-boot-starter-web'    // Web开发启动器(包含Tomcat、Spring MVC)
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // JPA数据访问启动器

    //
    // runtimeOnly:仅运行时需要的依赖,编译时不需要
    // 相当于Maven的runtime scope
    //
    runtimeOnly 'com.h2database:h2'                    // H2内存数据库,仅运行时需要
    runtimeOnly 'mysql:mysql-connector-java'           // MySQL驱动,仅运行时需要

    // 工具库:编译和运行时都需要
    implementation 'org.apache.commons:commons-lang3:3.12.0'  // Apache Commons工具库
    implementation 'com.fasterxml.jackson.core:jackson-databind' // JSON处理库

    //
    // testImplementation:测试时需要的依赖
    // 相当于Maven的test scope,仅在测试编译和运行时可用
    //
    testImplementation 'org.springframework.boot:spring-boot-starter-test' // Spring Boot测试启动器
    testImplementation 'org.junit.jupiter:junit-jupiter'       // JUnit 5测试框架
    testImplementation 'org.mockito:mockito-core'              // Mockito模拟框架

    //
    // 其他常用依赖配置:
    // api:类似implementation,但会传递给依赖此项目的其他项目
    // compileOnly:仅编译时需要,相当于Maven的provided
    // annotationProcessor:注解处理器,编译时处理注解
    //
}


//
// 应用程序配置:配置application插件的行为
//
application {
    // 指定应用程序的主类,用于gradle run命令
    mainClass = 'com.example.Application'
    // 其他可选配置:
    // applicationDefaultJvmArgs = ['-Xmx1g']  // 默认JVM参数
    // executableDir = 'bin'                   // 可执行文件目录
}

//
// 测试配置:配置测试任务的行为
//
test {
    // 使用JUnit Platform运行测试(支持JUnit 5)
    useJUnitPlatform()

    // 测试日志配置:控制测试执行时的输出信息
    testLogging {
        events "passed", "skipped", "failed"   // 显示通过、跳过、失败的测试
        // 其他可选配置:
        // exceptionFormat = 'full'             // 异常信息格式
        // showStandardStreams = true           // 显示标准输出
        // showCauses = true                    // 显示异常原因
    }

    // 其他测试配置:
    // maxParallelForks = 4                    // 并行测试进程数
    // forkEvery = 100                         // 每100个测试重启进程
    // systemProperty 'spring.profiles.active', 'test' // 设置系统属性
}

//
// 自定义任务:定义项目特定的构建任务
// Gradle的强大之处在于可以轻松定义自定义任务
//
task copyDocs(type: Copy) {                   // 定义一个复制类型的任务
    from 'src/main/doc'                       // 源目录
    into 'build/docs'                         // 目标目录
    // 其他Copy任务配置:
    // include '**/*.md'                      // 包含特定文件
    // exclude '**/temp/**'                   // 排除特定文件
    // rename '(.+).txt', '$1.backup'        // 重命名文件
}

// 可以定义更多自定义任务:
// task generateVersion {
//     doLast {
//         file('src/main/resources/version.txt').text = version
//     }
// }

//
// JAR配置:自定义JAR文件的生成
//
jar {
    // 配置JAR文件的MANIFEST.MF
    manifest {
        attributes(
            'Main-Class': 'com.example.Application',    // 主类
            'Implementation-Version': version,          // 实现版本
            'Implementation-Title': project.name,       // 实现标题
            'Built-By': System.getProperty('user.name'), // 构建者
            'Built-Date': new Date().toString(),        // 构建日期
            'Built-JDK': System.getProperty('java.version') // JDK版本
        )
    }

    // 其他JAR配置:
    // archiveBaseName = 'my-app'              // JAR文件基础名称
    // archiveVersion = '1.0.0'                // JAR文件版本
    // duplicatesStrategy = DuplicatesStrategy.EXCLUDE // 重复文件处理策略
}

//
// 多环境配置:根据不同环境加载不同的配置文件
// 使用方式:gradle build -Penv=prod
//
if (project.hasProperty('env')) {
    // 动态加载环境特定的配置文件
    apply from: "gradle/${project.env}.gradle"

    // 示例:gradle/dev.gradle 文件内容
    // ext {
    //     databaseUrl = 'jdbc:h2:mem:devdb'
    //     logLevel = 'DEBUG'
    // }

    // 示例:gradle/prod.gradle 文件内容
    // ext {
    //     databaseUrl = 'jdbc:mysql://prod-server:3306/proddb'
    //     logLevel = 'WARN'
    // }
}

📊 Maven vs Gradle 对比总结

特性MavenGradle
配置文件XML (pom.xml)Groovy/Kotlin DSL (build.gradle)
学习曲线相对简单,XML配置直观稍陡峭,但更灵活
构建速度较慢,特别是大项目更快,支持增量构建和并行执行
依赖管理成熟稳定,传递依赖处理好灵活强大,版本冲突解决更智能
插件生态非常丰富,历史悠久快速发展,现代化插件
企业采用广泛使用,企业首选快速增长,特别是新项目
IDE支持优秀,所有主流IDE都支持优秀,现代IDE都有良好支持
多模块项目支持良好支持优秀,配置更简洁
自定义任务通过插件,相对复杂非常灵活,易于自定义
构建缓存基础支持强大的构建缓存机制
🎯 选择建议

选择 Maven 的场景:

  • 团队对 XML 配置熟悉
  • 企业环境,需要稳定性
  • 项目相对简单,标准化程度高
  • 需要与现有 Maven 生态集成

选择 Gradle 的场景:

  • 追求构建性能和效率
  • 项目复杂,需要灵活的构建逻辑
  • 团队技术水平较高
  • Android 开发(Gradle 是官方构建工具)
  • 微服务架构,多模块项目
💡 最佳实践建议
  1. 新手建议:从 Maven 开始学习,掌握构建工具的基本概念
  2. 项目选择:根据团队技术栈和项目需求选择
  3. 混合使用:可以在不同项目中使用不同工具
  4. 持续学习:两种工具都在不断发展,保持学习

🧪 单元测试框架

🎯 JUnit 5详解

💻 JUnit 5基础测试

🎯 示例4:JUnit 5测试基础

import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*;

// 被测试的类
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("除数不能为0");
        }
        return a / b;
    }
    
    public boolean isPrime(int number) {
        if (number < 2) return false;
        for (int i = 2; i <= Math.sqrt(number); i++) {
            if (number % i == 0) return false;
        }
        return true;
    }
}

// 测试类
@DisplayName("计算器测试")
class CalculatorTest {
    
    private Calculator calculator;
    
    @BeforeAll
    static void setUpAll() {
        System.out.println("开始执行计算器测试套件");
    }
    
    @BeforeEach
    void setUp() {
        calculator = new Calculator();
        System.out.println("创建新的计算器实例");
    }
    
    @AfterEach
    void tearDown() {
        System.out.println("测试方法执行完毕");
    }
    
    @AfterAll
    static void tearDownAll() {
        System.out.println("计算器测试套件执行完毕");
    }
    
    @Test
    @DisplayName("测试加法运算")
    void testAdd() {
        // Given - 准备测试数据
        int a = 5;
        int b = 3;
        int expected = 8;
        
        // When - 执行被测试方法
        int result = calculator.add(a, b);
        
        // Then - 验证结果
        assertEquals(expected, result, "5 + 3 应该等于 8");
        assertTrue(result > 0, "结果应该大于0");
    }
    
    @Test
    @DisplayName("测试除法运算")
    void testDivide() {
        // 正常情况
        assertEquals(2, calculator.divide(6, 3));
        assertEquals(0, calculator.divide(0, 5));
        
        // 异常情况
        IllegalArgumentException exception = assertThrows(
            IllegalArgumentException.class,
            () -> calculator.divide(10, 0),
            "除数为0应该抛出异常"
        );
        
        assertEquals("除数不能为0", exception.getMessage());
    }
    
    @ParameterizedTest
    @DisplayName("参数化测试 - 质数判断")
    @ValueSource(ints = {2, 3, 5, 7, 11, 13, 17, 19})
    void testIsPrime(int number) {
        assertTrue(calculator.isPrime(number), number + " 应该是质数");
    }
    
    @ParameterizedTest
    @DisplayName("参数化测试 - 加法运算")
    @CsvSource({
        "1, 2, 3",
        "5, 5, 10",
        "-1, 1, 0",
        "0, 0, 0"
    })
    void testAddWithCsv(int a, int b, int expected) {
        assertEquals(expected, calculator.add(a, b));
    }
    
    @Test
    @DisplayName("条件测试 - 仅在特定环境下运行")
    void testOnlyOnDevelopment() {
        assumeTrue("dev".equals(System.getProperty("env")), 
                  "仅在开发环境下运行此测试");
        
        // 测试逻辑
        assertNotNull(calculator);
    }
    
    @RepeatedTest(5)
    @DisplayName("重复测试")
    void testRepeated(RepetitionInfo repetitionInfo) {
        System.out.println("执行第 " + repetitionInfo.getCurrentRepetition() + 
                          " 次,共 " + repetitionInfo.getTotalRepetitions() + " 次");
        assertTrue(calculator.add(1, 1) == 2);
    }
    
    @Nested
    @DisplayName("边界值测试")
    class BoundaryTests {
        
        @Test
        @DisplayName("测试最大整数相加")
        void testMaxIntegerAdd() {
            // 注意:这可能导致整数溢出
            assertThrows(ArithmeticException.class, () -> {
                Math.addExact(Integer.MAX_VALUE, 1);
            });
        }
        
        @Test
        @DisplayName("测试负数运算")
        void testNegativeNumbers() {
            assertEquals(-2, calculator.add(-5, 3));
            assertEquals(-8, calculator.add(-5, -3));
        }
    }
}
🔧 Mockito测试框架

🎯 示例5:Mockito模拟测试

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

// 用户服务接口
interface UserRepository {
    User findById(Long id);
    void save(User user);
    boolean existsByEmail(String email);
}

// 邮件服务接口
interface EmailService {
    void sendWelcomeEmail(String email, String name);
}

// 用户实体
class User {
    private Long id;
    private String name;
    private String email;

    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // getter和setter方法
    public Long getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }
    public void setId(Long id) { this.id = id; }
}

// 用户服务类
class UserService {
    private final UserRepository userRepository;
    private final EmailService emailService;

    public UserService(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }

    public User createUser(String name, String email) {
        if (userRepository.existsByEmail(email)) {
            throw new IllegalArgumentException("邮箱已存在");
        }

        User user = new User(null, name, email);
        userRepository.save(user);
        emailService.sendWelcomeEmail(email, name);

        return user;
    }

    public User getUserById(Long id) {
        User user = userRepository.findById(id);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        return user;
    }
}

// Mockito测试类
@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @Mock
    private EmailService emailService;

    @InjectMocks
    private UserService userService;

    @Test
    void testCreateUser_Success() {
        // Given
        String name = "张三";
        String email = "zhangsan@example.com";

        when(userRepository.existsByEmail(email)).thenReturn(false);

        // When
        User result = userService.createUser(name, email);

        // Then
        assertNotNull(result);
        assertEquals(name, result.getName());
        assertEquals(email, result.getEmail());

        // 验证方法调用
        verify(userRepository).existsByEmail(email);
        verify(userRepository).save(any(User.class));
        verify(emailService).sendWelcomeEmail(email, name);
    }

    @Test
    void testCreateUser_EmailExists() {
        // Given
        String name = "李四";
        String email = "lisi@example.com";

        when(userRepository.existsByEmail(email)).thenReturn(true);

        // When & Then
        IllegalArgumentException exception = assertThrows(
            IllegalArgumentException.class,
            () -> userService.createUser(name, email)
        );

        assertEquals("邮箱已存在", exception.getMessage());

        // 验证没有调用保存和发邮件
        verify(userRepository, never()).save(any(User.class));
        verify(emailService, never()).sendWelcomeEmail(anyString(), anyString());
    }

    @Test
    void testGetUserById_Success() {
        // Given
        Long userId = 1L;
        User expectedUser = new User(userId, "王五", "wangwu@example.com");

        when(userRepository.findById(userId)).thenReturn(expectedUser);

        // When
        User result = userService.getUserById(userId);

        // Then
        assertEquals(expectedUser, result);
        verify(userRepository).findById(userId);
    }

    @Test
    void testGetUserById_NotFound() {
        // Given
        Long userId = 999L;
        when(userRepository.findById(userId)).thenReturn(null);

        // When & Then
        RuntimeException exception = assertThrows(
            RuntimeException.class,
            () -> userService.getUserById(userId)
        );

        assertEquals("用户不存在", exception.getMessage());
    }
}

📝 日志框架应用

🎯 日志框架概述

🌟 日志框架:记录应用程序运行时信息的工具,是调试、监控和故障排查的重要手段

✨ 日志框架的重要性
作用说明应用场景
🐛 调试支持记录程序执行流程开发阶段问题定位
📊 运行监控监控系统运行状态生产环境性能分析
🚨 错误追踪记录异常和错误信息故障排查和修复
📈 业务分析记录业务操作日志用户行为分析
🔍 审计合规记录关键操作安全审计要求

💻 SLF4J + Logback实战

🎯 日志配置和使用

🎯 示例6:Logback配置和使用

<!--
    logback-spring.xml - Logback日志框架配置文件
    文件名说明:
    - logback.xml: 标准Logback配置文件
    - logback-spring.xml: Spring Boot专用配置文件,支持Spring Profile等特性
-->
<?xml version="1.0" encoding="UTF-8"?>
<!--
    configuration: Logback配置的根元素
    常用属性:
    - scan="true": 自动扫描配置文件变化
    - scanPeriod="30 seconds": 扫描间隔
    - debug="false": 是否输出Logback内部调试信息
-->
<configuration>

    <!--
        属性定义:定义可重用的变量,类似于编程中的常量
        可以在配置文件的其他地方使用${属性名}引用
    -->
    <property name="LOG_HOME" value="logs"/>        <!-- 日志文件存储目录 -->
    <property name="APP_NAME" value="my-app"/>      <!-- 应用程序名称,用于日志文件命名 -->

    <!--
        控制台输出配置(Appender)
        Appender:日志输出目的地,可以是控制台、文件、数据库等
        ConsoleAppender:将日志输出到控制台(System.out或System.err)
    -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--
            编码器(Encoder):负责将日志事件转换为字节数组
            PatternLayoutEncoder:使用模式字符串格式化日志
        -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--
                日志输出格式模式:
                %d{yyyy-MM-dd HH:mm:ss.SSS}: 日期时间,精确到毫秒
                [%thread]: 线程名,用方括号包围
                %-5level: 日志级别,左对齐,占5个字符位置
                %logger{50}: 记录器名称,最多显示50个字符
                %msg: 日志消息内容
                %n: 换行符(跨平台兼容)
            -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <!-- 字符编码:确保中文等特殊字符正确显示 -->
            <charset>UTF-8</charset>
        </encoder>
        <!--
            其他可选配置:
            <target>System.out</target>: 输出目标,默认System.out
            <withJansi>true</withJansi>: 在Windows上启用颜色输出
        -->
    </appender>

    <!--
        文件输出配置
        RollingFileAppender:支持日志文件滚动的文件输出器
        当文件达到一定大小或时间时,会创建新文件,避免单个文件过大
    -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 当前活动的日志文件路径 -->
        <file>${LOG_HOME}/${APP_NAME}.log</file>

        <!--
            滚动策略:定义何时创建新的日志文件
            TimeBasedRollingPolicy:基于时间的滚动策略
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
                文件名模式:定义归档文件的命名规则
                %d{yyyy-MM-dd}:按天滚动,日期格式
                %i:同一天内的文件序号(当文件大小超限时)
                示例:my-app.2024-01-15.0.log, my-app.2024-01-15.1.log
            -->
            <fileNamePattern>${LOG_HOME}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>

            <!--
                基于大小和时间的触发策略
                当文件大小达到maxFileSize时,即使还没到下一天也会滚动
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>    <!-- 单个文件最大大小 -->
            </timeBasedFileNamingAndTriggeringPolicy>

            <!-- 保留历史文件的天数,超过此天数的文件会被自动删除 -->
            <maxHistory>30</maxHistory>

            <!--
                所有日志文件的总大小上限
                当总大小超过此值时,会删除最旧的文件
            -->
            <totalSizeCap>3GB</totalSizeCap>

            <!--
                其他可选配置:
                <cleanHistoryOnStart>true</cleanHistoryOnStart>: 启动时清理历史文件
            -->
        </rollingPolicy>

        <!-- 文件输出的编码器配置 -->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>

        <!--
            其他可选配置:
            <append>true</append>: 是否追加到现有文件
            <immediateFlush>true</immediateFlush>: 是否立即刷新到磁盘
            <prudent>false</prudent>: 是否启用谨慎模式(多JVM安全)
        -->
    </appender>

    <!--
        错误日志单独输出
        将ERROR级别的日志单独输出到专门的错误日志文件
        便于快速定位和分析系统错误
    -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 错误日志文件路径 -->
        <file>${LOG_HOME}/${APP_NAME}-error.log</file>

        <!--
            日志级别过滤器:只记录ERROR级别的日志
            LevelFilter:精确匹配特定级别的过滤器
        -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>                    <!-- 过滤的日志级别 -->
            <onMatch>ACCEPT</onMatch>               <!-- 匹配时的动作:接受 -->
            <onMismatch>DENY</onMismatch>           <!-- 不匹配时的动作:拒绝 -->
        </filter>

        <!-- 简化的滚动策略:只按时间滚动 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/${APP_NAME}-error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>             <!-- 保留30天的错误日志 -->
        </rollingPolicy>

        <encoder>
            <!-- 错误日志可以包含更详细的信息,如堆栈跟踪 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--
        异步日志配置
        AsyncAppender:异步日志输出器,提高日志性能
        将日志事件放入队列,由后台线程异步写入,避免阻塞业务线程
    -->
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <!--
            丢弃阈值:当队列使用率超过此值时,开始丢弃TRACE、DEBUG、INFO级别的日志
            0表示不丢弃任何日志,100表示队列满时才丢弃
        -->
        <discardingThreshold>0</discardingThreshold>

        <!--
            队列大小:异步日志事件队列的容量
            队列越大,能缓存的日志越多,但占用内存也越多
        -->
        <queueSize>1024</queueSize>

        <!-- 引用的同步appender:异步包装的目标appender -->
        <appender-ref ref="FILE"/>

        <!--
            其他可选配置:
            <maxFlushTime>1000</maxFlushTime>: 关闭时等待队列清空的最大时间(毫秒)
            <neverBlock>false</neverBlock>: 队列满时是否阻塞调用线程
            <includeCallerData>false</includeCallerData>: 是否包含调用者信息(影响性能)
        -->
    </appender>

    <!--
        特定包的日志级别配置
        Logger:为特定的包或类配置独立的日志级别和输出方式
    -->
    <logger name="com.example.service" level="DEBUG" additivity="false">
        <!--
            name:Logger的名称,通常是包名或类名
            level:该Logger的日志级别,会覆盖root logger的级别
            additivity:是否继承父Logger的appender
            - true(默认):会同时使用父Logger和当前Logger的appender
            - false:只使用当前Logger的appender,避免重复输出
        -->
        <appender-ref ref="CONSOLE"/>       <!-- 输出到控制台 -->
        <appender-ref ref="ASYNC_FILE"/>    <!-- 输出到文件 -->
    </logger>

    <!-- Spring框架的日志级别:通常设置为INFO,避免过多调试信息 -->
    <logger name="org.springframework" level="INFO"/>

    <!-- Hibernate ORM框架的日志级别:设置为WARN,只显示警告和错误 -->
    <logger name="org.hibernate" level="WARN"/>

    <!--
        其他常用框架的日志配置示例:
        <logger name="org.apache.http" level="WARN"/>        HTTP客户端
        <logger name="com.zaxxer.hikari" level="WARN"/>      连接池
        <logger name="org.mybatis" level="DEBUG"/>          MyBatis(开发时可设为DEBUG查看SQL)
    -->

    <!--
        根日志配置(Root Logger)
        所有没有特定配置的Logger都会继承root logger的配置
        这是日志配置的基础和默认设置
    -->
    <root level="INFO">
        <!-- 根Logger使用的appender,所有日志都会输出到这些目的地 -->
        <appender-ref ref="CONSOLE"/>       <!-- 控制台输出 -->
        <appender-ref ref="ASYNC_FILE"/>    <!-- 异步文件输出 -->
        <appender-ref ref="ERROR_FILE"/>    <!-- 错误日志文件 -->
    </root>

    <!--
        环境特定配置(Spring Profile)
        根据不同的运行环境使用不同的日志配置
        Spring Boot会根据spring.profiles.active属性激活相应的配置
    -->

    <!-- 开发环境配置:详细的日志输出,主要输出到控制台 -->
    <springProfile name="dev">
        <root level="DEBUG">                <!-- 开发环境使用DEBUG级别 -->
            <appender-ref ref="CONSOLE"/>   <!-- 开发时主要看控制台 -->
        </root>
        <!-- 开发环境可以启用SQL日志 -->
        <logger name="org.hibernate.SQL" level="DEBUG"/>
        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
    </springProfile>

    <!-- 生产环境配置:精简的日志输出,主要输出到文件 -->
    <springProfile name="prod">
        <root level="WARN">                 <!-- 生产环境只记录警告和错误 -->
            <appender-ref ref="ASYNC_FILE"/><!-- 生产环境主要记录到文件 -->
            <appender-ref ref="ERROR_FILE"/><!-- 错误日志单独记录 -->
        </root>
        <!-- 生产环境关闭详细的框架日志 -->
        <logger name="org.springframework" level="WARN"/>
        <logger name="org.hibernate" level="ERROR"/>
    </springProfile>

    <!--
        测试环境配置示例:
        <springProfile name="test">
            <root level="WARN">
                <appender-ref ref="CONSOLE"/>
            </root>
        </springProfile>
    -->

</configuration>
💻 Java代码中使用日志

🎯 示例7:SLF4J日志使用实践

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class LoggingDemo {

    // 创建日志记录器
    private static final Logger logger = LoggerFactory.getLogger(LoggingDemo.class);

    public static void main(String[] args) {
        System.out.println("=== 日志框架使用演示 ===");

        // 1. 基本日志级别
        demonstrateLogLevels();

        // 2. 参数化日志
        demonstrateParameterizedLogging();

        // 3. 异常日志
        demonstrateExceptionLogging();

        // 4. MDC使用
        demonstrateMDC();

        // 5. 性能日志
        demonstratePerformanceLogging();
    }

    private static void demonstrateLogLevels() {
        logger.info("=== 日志级别演示 ===");

        // TRACE - 最详细的信息,通常只在开发时使用
        logger.trace("这是TRACE级别日志 - 非常详细的执行信息");

        // DEBUG - 调试信息
        logger.debug("这是DEBUG级别日志 - 调试信息");

        // INFO - 一般信息
        logger.info("这是INFO级别日志 - 一般信息");

        // WARN - 警告信息
        logger.warn("这是WARN级别日志 - 警告信息");

        // ERROR - 错误信息
        logger.error("这是ERROR级别日志 - 错误信息");
    }

    private static void demonstrateParameterizedLogging() {
        logger.info("=== 参数化日志演示 ===");

        String username = "张三";
        int age = 25;
        double salary = 8500.50;

        // ❌ 错误方式 - 字符串拼接
        // logger.info("用户信息: " + username + ", 年龄: " + age + ", 薪资: " + salary);

        // ✅ 正确方式 - 参数化日志
        logger.info("用户信息: {}, 年龄: {}, 薪资: {}", username, age, salary);

        // 多个参数
        logger.info("处理用户 {} 的订单,订单号: {}, 金额: {}, 状态: {}",
                   username, "ORD001", 299.99, "已支付");

        // 条件日志 - 避免不必要的字符串操作
        if (logger.isDebugEnabled()) {
            String expensiveOperation = performExpensiveStringOperation();
            logger.debug("调试信息: {}", expensiveOperation);
        }
    }

    private static void demonstrateExceptionLogging() {
        logger.info("=== 异常日志演示 ===");

        try {
            // 模拟可能抛出异常的操作
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            // 记录异常信息
            logger.error("计算过程中发生错误", e);

            // 带上下文信息的异常日志
            logger.error("用户 {} 执行计算操作时发生错误: {}", "张三", e.getMessage(), e);
        }

        try {
            processUserData(null);
        } catch (IllegalArgumentException e) {
            logger.warn("用户数据验证失败: {}", e.getMessage());
        }
    }

    private static void demonstrateMDC() {
        logger.info("=== MDC (Mapped Diagnostic Context) 演示 ===");

        // 设置MDC上下文信息
        MDC.put("userId", "12345");
        MDC.put("requestId", "REQ-" + System.currentTimeMillis());
        MDC.put("sessionId", "SES-ABC123");

        try {
            logger.info("开始处理用户请求");

            // 模拟业务处理
            processBusinessLogic();

            logger.info("用户请求处理完成");

        } finally {
            // 清理MDC上下文
            MDC.clear();
        }
    }

    private static void demonstratePerformanceLogging() {
        logger.info("=== 性能日志演示 ===");

        String operationName = "数据库查询";
        long startTime = System.currentTimeMillis();

        try {
            // 模拟耗时操作
            Thread.sleep(100);

            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;

            // 记录性能信息
            logger.info("操作 {} 执行完成,耗时: {}ms", operationName, duration);

            // 性能警告
            if (duration > 1000) {
                logger.warn("操作 {} 执行时间过长: {}ms,需要优化", operationName, duration);
            }

        } catch (InterruptedException e) {
            logger.error("操作 {} 被中断", operationName, e);
            Thread.currentThread().interrupt();
        }
    }

    private static void processBusinessLogic() {
        logger.debug("执行业务逻辑步骤1");
        logger.debug("执行业务逻辑步骤2");
        logger.debug("执行业务逻辑步骤3");
    }

    private static void processUserData(String userData) {
        if (userData == null) {
            throw new IllegalArgumentException("用户数据不能为空");
        }
        logger.info("处理用户数据: {}", userData);
    }

    private static String performExpensiveStringOperation() {
        // 模拟耗时的字符串操作
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 1000; i++) {
            sb.append("expensive_operation_").append(i).append(" ");
        }
        return sb.toString();
    }
}

🌐 Web开发基础

🎯 Servlet基础

🌟 Servlet:Java Web开发的基础技术,用于处理HTTP请求和响应

💻 Servlet基础示例

🎯 示例8:Servlet基础开发

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

// 基础Servlet示例
// @WebServlet注解:Servlet 3.0+引入的注解配置方式,替代web.xml配置
@WebServlet(
    name = "HelloServlet",                      // Servlet名称,用于标识
    urlPatterns = {"/hello", "/greeting"}       // URL映射模式,支持多个路径
    // 其他可选属性:
    // value = {"/hello"}                       // urlPatterns的简写形式
    // initParams = {@WebInitParam(name="key", value="value")} // 初始化参数
    // loadOnStartup = 1                        // 启动时加载顺序,正数表示启动时加载
    // asyncSupported = true                    // 是否支持异步处理
    // description = "Hello Servlet"            // 描述信息
)
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 设置响应内容类型
        response.setContentType("text/html;charset=UTF-8");

        // 获取请求参数
        String name = request.getParameter("name");
        if (name == null || name.trim().isEmpty()) {
            name = "访客";
        }

        // 写入响应
        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE html>");
        out.println("<html>");
        out.println("<head><title>Hello Servlet</title></head>");
        out.println("<body>");
        out.println("<h1>你好, " + name + "!</h1>");
        out.println("<p>当前时间: " + new Date() + "</p>");
        out.println("</body>");
        out.println("</html>");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 设置请求编码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=UTF-8");

        // 获取表单数据
        String username = request.getParameter("username");
        String email = request.getParameter("email");

        // 简单验证
        if (username == null || username.trim().isEmpty()) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().write("{\"error\": \"用户名不能为空\"}");
            return;
        }

        // 模拟保存用户
        User user = new User(username, email);

        // 返回JSON响应
        String jsonResponse = String.format(
            "{\"success\": true, \"message\": \"用户 %s 创建成功\", \"userId\": %d}",
            username, user.getId()
        );

        response.getWriter().write(jsonResponse);
    }
}

// 用户实体类
class User {
    private static long idCounter = 1;
    private long id;
    private String username;
    private String email;

    public User(String username, String email) {
        this.id = idCounter++;
        this.username = username;
        this.email = email;
    }

    // getter方法
    public long getId() { return id; }
    public String getUsername() { return username; }
    public String getEmail() { return email; }
}

// 过滤器示例
// @WebFilter注解:配置Servlet过滤器,用于拦截和处理HTTP请求
@WebFilter(
    filterName = "LoggingFilter",               // 过滤器名称
    urlPatterns = {"/*"}                        // URL匹配模式,/*表示拦截所有请求
    // 其他可选属性:
    // value = {"/*"}                           // urlPatterns的简写形式
    // servletNames = {"HelloServlet"}          // 指定要过滤的Servlet名称
    // dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD} // 分发类型
    // initParams = {@WebInitParam(name="key", value="value")} // 初始化参数
    // asyncSupported = true                    // 是否支持异步处理
    // description = "Logging Filter"           // 描述信息
)
public class LoggingFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("LoggingFilter 初始化");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                        FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        long startTime = System.currentTimeMillis();
        String method = httpRequest.getMethod();
        String uri = httpRequest.getRequestURI();
        String queryString = httpRequest.getQueryString();

        logger.info("请求开始: {} {} {}", method, uri,
                   queryString != null ? "?" + queryString : "");

        try {
            // 继续执行请求
            chain.doFilter(request, response);
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            int status = httpResponse.getStatus();

            logger.info("请求完成: {} {} - 状态: {}, 耗时: {}ms",
                       method, uri, status, duration);
        }
    }

    @Override
    public void destroy() {
        logger.info("LoggingFilter 销毁");
    }
}

// 监听器示例
// @WebListener注解:配置Web应用监听器,监听应用生命周期事件
@WebListener
// 可选属性(较少使用):
// @WebListener(value = "Application Listener")  // 描述信息
public class ApplicationListener implements ServletContextListener,    // 应用上下文监听器
                                           HttpSessionListener {       // HTTP会话监听器
// 常用的监听器接口:
// - ServletContextListener: 监听应用启动和关闭
// - HttpSessionListener: 监听会话创建和销毁
// - ServletRequestListener: 监听请求创建和销毁
// - HttpSessionAttributeListener: 监听会话属性变化
// - ServletContextAttributeListener: 监听应用属性变化

    private static final Logger logger = LoggerFactory.getLogger(ApplicationListener.class);

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        logger.info("Web应用启动");

        // 初始化应用级别的资源
        ServletContext context = sce.getServletContext();
        context.setAttribute("startTime", new Date());
        context.setAttribute("visitCount", 0);
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        logger.info("Web应用关闭");

        // 清理资源
    }

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        logger.info("新会话创建: {}", se.getSession().getId());

        // 增加访问计数
        ServletContext context = se.getSession().getServletContext();
        Integer visitCount = (Integer) context.getAttribute("visitCount");
        context.setAttribute("visitCount", visitCount + 1);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        logger.info("会话销毁: {}", se.getSession().getId());
    }
}

🚀 Spring Boot入门

🎯 Spring Boot基础应用

🎯 示例9:Spring Boot Web应用

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.stereotype.Repository;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

// 主应用类
// @SpringBootApplication:Spring Boot应用的核心注解,组合了三个重要注解
@SpringBootApplication
// 等价于以下三个注解的组合:
// @Configuration:标识这是一个配置类,可以定义Bean
// @EnableAutoConfiguration:启用Spring Boot的自动配置机制
// @ComponentScan:启用组件扫描,自动发现和注册Bean
//
// 可选属性:
// @SpringBootApplication(
//     scanBasePackages = {"com.example", "com.other"}  // 指定扫描的包
//     exclude = {DataSourceAutoConfiguration.class}    // 排除特定的自动配置
// )
public class WebApplication {
    public static void main(String[] args) {
        // SpringApplication.run():启动Spring Boot应用
        // 会创建Spring应用上下文,启动嵌入式Web服务器(如Tomcat)
        SpringApplication.run(WebApplication.class, args);
    }
}

// 用户实体
class User {
    private Long id;
    private String name;
    private String email;
    private Date createdAt;

    public User() {}

    public User(String name, String email) {
        this.name = name;
        this.email = email;
        this.createdAt = new Date();
    }

    // getter和setter方法
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public Date getCreatedAt() { return createdAt; }
    public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; }
}

// 数据访问层
// @Repository:数据访问层注解,标识数据访问组件(DAO层)
@Repository
// Spring的分层注解体系:
// @Component:通用组件注解,是其他注解的基础
// @Repository:数据访问层,继承自@Component,额外提供数据访问异常转换
// @Service:业务逻辑层,继承自@Component
// @Controller:控制层,继承自@Component
// @RestController:REST控制层,组合了@Controller和@ResponseBody
//
// @Repository的特殊功能:
// 1. 自动注册为Spring Bean
// 2. 提供数据访问异常转换(将底层异常转换为Spring的DataAccessException)
// 3. 支持Spring Data JPA等数据访问技术的自动代理
public class UserRepository {

    private final Map<Long, User> users = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(1);

    public User save(User user) {
        if (user.getId() == null) {
            user.setId(idGenerator.getAndIncrement());
        }
        users.put(user.getId(), user);
        return user;
    }

    public Optional<User> findById(Long id) {
        return Optional.ofNullable(users.get(id));
    }

    public List<User> findAll() {
        return new ArrayList<>(users.values());
    }

    public void deleteById(Long id) {
        users.remove(id);
    }

    public boolean existsByEmail(String email) {
        return users.values().stream()
                   .anyMatch(user -> email.equals(user.getEmail()));
    }
}

// 业务逻辑层
// @Service:业务逻辑层注解,标识业务服务组件
@Service
// @Service注解的作用:
// 1. 继承自@Component,会被Spring自动扫描并注册为Bean
// 2. 语义化标识:明确表示这是业务逻辑层组件
// 3. 便于AOP切面编程:可以针对@Service组件进行统一的事务管理、日志记录等
// 4. 便于分层架构管理:清晰的分层结构,便于维护和测试
//
// 可选属性:
// @Service("userService")  // 指定Bean的名称,默认为类名首字母小写
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public User createUser(String name, String email) {
        if (userRepository.existsByEmail(email)) {
            throw new IllegalArgumentException("邮箱已存在: " + email);
        }

        User user = new User(name, email);
        return userRepository.save(user);
    }

    public Optional<User> getUserById(Long id) {
        return userRepository.findById(id);
    }

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    public User updateUser(Long id, String name, String email) {
        User user = userRepository.findById(id)
                                 .orElseThrow(() -> new RuntimeException("用户不存在"));

        if (!email.equals(user.getEmail()) && userRepository.existsByEmail(email)) {
            throw new IllegalArgumentException("邮箱已存在: " + email);
        }

        user.setName(name);
        user.setEmail(email);
        return userRepository.save(user);
    }

    public void deleteUser(Long id) {
        if (!userRepository.findById(id).isPresent()) {
            throw new RuntimeException("用户不存在");
        }
        userRepository.deleteById(id);
    }
}

// 控制器层
// @RestController:RESTful Web服务控制器注解,组合了@Controller和@ResponseBody
@RestController
// 等价于:
// @Controller + @ResponseBody(在每个方法上)
// 表示这个类的所有方法返回值都会直接写入HTTP响应体,通常返回JSON格式

// @RequestMapping:映射HTTP请求到控制器类或方法
@RequestMapping("/api/users")               // 类级别的映射,所有方法都会继承这个路径前缀
// 可选属性:
// @RequestMapping(
//     value = "/api/users",                // 请求路径
//     method = RequestMethod.GET,          // HTTP方法限制
//     produces = "application/json",       // 响应内容类型
//     consumes = "application/json",       // 接受的内容类型
//     headers = "Accept=application/json", // 请求头限制
//     params = "version=1"                 // 请求参数限制
// )
public class UserController {

    // @Autowired:自动装配注解,Spring会自动注入匹配的Bean
    @Autowired
    // 其他注入方式:
    // 1. 构造器注入(推荐):
    //    private final UserService userService;
    //    public UserController(UserService userService) { this.userService = userService; }
    // 2. Setter注入:
    //    @Autowired public void setUserService(UserService userService) { ... }
    // 3. @Resource:按名称注入
    // 4. @Qualifier:指定具体的Bean名称
    private UserService userService;

    // 获取所有用户
    // @GetMapping:处理HTTP GET请求的简化注解
    @GetMapping                                     // 等价于 @RequestMapping(method = RequestMethod.GET)
    // 完整路径:GET /api/users(继承类级别的@RequestMapping)
    public ResponseEntity<List<User>> getAllUsers() {
        List<User> users = userService.getAllUsers();
        // ResponseEntity:Spring提供的HTTP响应封装类,可以设置状态码、头部等
        return ResponseEntity.ok(users);           // 返回200 OK状态码和用户列表
    }

    // 根据ID获取用户
    // @GetMapping:支持路径变量的GET请求映射
    @GetMapping("/{id}")                           // 路径变量模式,{id}是占位符
    // 完整路径:GET /api/users/{id},如 GET /api/users/123
    public ResponseEntity<User> getUserById(
            @PathVariable Long id                  // @PathVariable:绑定路径变量到方法参数
            // 可选属性:
            // @PathVariable("userId") Long id     // 指定路径变量名称
            // @PathVariable(required = false)     // 设置为可选参数
    ) {
        Optional<User> user = userService.getUserById(id);
        return user.map(ResponseEntity::ok)        // 找到用户返回200 OK
                  .orElse(ResponseEntity.notFound().build()); // 未找到返回404 Not Found
    }

    // 创建用户
    // @PostMapping:处理HTTP POST请求,通常用于创建资源
    @PostMapping                                   // 等价于 @RequestMapping(method = RequestMethod.POST)
    // 完整路径:POST /api/users
    public ResponseEntity<?> createUser(
            @RequestBody CreateUserRequest request // @RequestBody:将HTTP请求体绑定到方法参数
            // Spring会自动将JSON转换为Java对象(使用Jackson)
            // 可选属性:
            // @RequestBody(required = false)      // 设置为可选参数
    ) {
        try {
            User user = userService.createUser(request.getName(), request.getEmail());
            // 201 Created:表示资源创建成功
            return ResponseEntity.status(HttpStatus.CREATED).body(user);
        } catch (IllegalArgumentException e) {
            // 400 Bad Request:表示客户端请求错误
            return ResponseEntity.badRequest()
                                .body(Map.of("error", e.getMessage()));
        }
    }

    // 更新用户
    // @PutMapping:处理HTTP PUT请求,通常用于更新整个资源
    @PutMapping("/{id}")                           // 等价于 @RequestMapping(method = RequestMethod.PUT)
    // 完整路径:PUT /api/users/{id}
    public ResponseEntity<?> updateUser(
            @PathVariable Long id,                 // 路径变量:用户ID
            @RequestBody UpdateUserRequest request // 请求体:更新的用户信息
    ) {
        try {
            User user = userService.updateUser(id, request.getName(), request.getEmail());
            return ResponseEntity.ok(user);       // 200 OK:更新成功
        } catch (RuntimeException e) {
            return ResponseEntity.notFound().build(); // 404 Not Found:用户不存在
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest()    // 400 Bad Request:参数错误
                                .body(Map.of("error", e.getMessage()));
        }
    }

    // 删除用户
    // @DeleteMapping:处理HTTP DELETE请求,用于删除资源
    @DeleteMapping("/{id}")                        // 等价于 @RequestMapping(method = RequestMethod.DELETE)
    // 完整路径:DELETE /api/users/{id}
    public ResponseEntity<?> deleteUser(@PathVariable Long id) {
        try {
            userService.deleteUser(id);
            // 204 No Content:删除成功,无返回内容
            return ResponseEntity.noContent().build();
        } catch (RuntimeException e) {
            return ResponseEntity.notFound().build(); // 404 Not Found:用户不存在
        }
    }

    // 其他常用HTTP方法映射注解:
    // @PatchMapping:处理PATCH请求,用于部分更新资源
    // @RequestMapping:通用映射注解,可以指定多种HTTP方法
    //
    // 常用参数注解:
    // @RequestParam:绑定查询参数,如 ?name=value
    // @RequestHeader:绑定请求头
    // @CookieValue:绑定Cookie值
    // @ModelAttribute:绑定表单数据到对象
}

// 请求DTO类
class CreateUserRequest {
    private String name;
    private String email;

    // getter和setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

class UpdateUserRequest {
    private String name;
    private String email;

    // getter和setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

🗄️ 数据库连接与ORM

🎯 JDBC基础

💻 JDBC连接和操作

🎯 示例10:JDBC数据库操作

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class JDBCDemo {

    private static final String DB_URL = "jdbc:h2:mem:testdb";
    private static final String DB_USER = "sa";
    private static final String DB_PASSWORD = "";

    public static void main(String[] args) {
        try {
            // 初始化数据库
            initDatabase();

            // 演示CRUD操作
            demonstrateCRUD();

        } catch (SQLException e) {
            System.err.println("数据库操作失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private static void initDatabase() throws SQLException {
        try (Connection conn = getConnection();
             Statement stmt = conn.createStatement()) {

            // 创建用户表
            String createTableSQL = """
                CREATE TABLE users (
                    id BIGINT AUTO_INCREMENT PRIMARY KEY,
                    name VARCHAR(100) NOT NULL,
                    email VARCHAR(100) UNIQUE NOT NULL,
                    age INT,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                )
                """;

            stmt.executeUpdate(createTableSQL);
            System.out.println("数据库表创建成功");
        }
    }

    private static void demonstrateCRUD() throws SQLException {
        UserDAO userDAO = new UserDAO();

        // 创建用户
        System.out.println("\n=== 创建用户 ===");
        User user1 = userDAO.create(new User("张三", "zhangsan@example.com", 25));
        User user2 = userDAO.create(new User("李四", "lisi@example.com", 30));
        System.out.println("创建用户: " + user1);
        System.out.println("创建用户: " + user2);

        // 查询用户
        System.out.println("\n=== 查询用户 ===");
        User foundUser = userDAO.findById(user1.getId());
        System.out.println("查询到用户: " + foundUser);

        List<User> allUsers = userDAO.findAll();
        System.out.println("所有用户: " + allUsers);

        // 更新用户
        System.out.println("\n=== 更新用户 ===");
        user1.setAge(26);
        userDAO.update(user1);
        System.out.println("更新后用户: " + userDAO.findById(user1.getId()));

        // 删除用户
        System.out.println("\n=== 删除用户 ===");
        userDAO.delete(user2.getId());
        System.out.println("删除后剩余用户: " + userDAO.findAll());
    }

    private static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
    }
}

// 用户实体类
class User {
    private Long id;
    private String name;
    private String email;
    private Integer age;
    private Timestamp createdAt;

    public User() {}

    public User(String name, String email, Integer age) {
        this.name = name;
        this.email = email;
        this.age = age;
    }

    // getter和setter方法
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    public Timestamp getCreatedAt() { return createdAt; }
    public void setCreatedAt(Timestamp createdAt) { this.createdAt = createdAt; }

    @Override
    public String toString() {
        return String.format("User{id=%d, name='%s', email='%s', age=%d, createdAt=%s}",
                           id, name, email, age, createdAt);
    }
}

// 数据访问对象
class UserDAO {

    private static final String DB_URL = "jdbc:h2:mem:testdb";
    private static final String DB_USER = "sa";
    private static final String DB_PASSWORD = "";

    public User create(User user) throws SQLException {
        String sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";

        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {

            pstmt.setString(1, user.getName());
            pstmt.setString(2, user.getEmail());
            pstmt.setInt(3, user.getAge());

            int affectedRows = pstmt.executeUpdate();
            if (affectedRows == 0) {
                throw new SQLException("创建用户失败,没有行被影响");
            }

            try (ResultSet generatedKeys = pstmt.getGeneratedKeys()) {
                if (generatedKeys.next()) {
                    user.setId(generatedKeys.getLong(1));
                } else {
                    throw new SQLException("创建用户失败,无法获取ID");
                }
            }
        }

        return user;
    }

    public User findById(Long id) throws SQLException {
        String sql = "SELECT * FROM users WHERE id = ?";

        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setLong(1, id);

            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    return mapResultSetToUser(rs);
                }
            }
        }

        return null;
    }

    public List<User> findAll() throws SQLException {
        String sql = "SELECT * FROM users ORDER BY id";
        List<User> users = new ArrayList<>();

        try (Connection conn = getConnection();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {

            while (rs.next()) {
                users.add(mapResultSetToUser(rs));
            }
        }

        return users;
    }

    public void update(User user) throws SQLException {
        String sql = "UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?";

        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setString(1, user.getName());
            pstmt.setString(2, user.getEmail());
            pstmt.setInt(3, user.getAge());
            pstmt.setLong(4, user.getId());

            int affectedRows = pstmt.executeUpdate();
            if (affectedRows == 0) {
                throw new SQLException("更新用户失败,用户不存在");
            }
        }
    }

    public void delete(Long id) throws SQLException {
        String sql = "DELETE FROM users WHERE id = ?";

        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setLong(1, id);

            int affectedRows = pstmt.executeUpdate();
            if (affectedRows == 0) {
                throw new SQLException("删除用户失败,用户不存在");
            }
        }
    }

    private User mapResultSetToUser(ResultSet rs) throws SQLException {
        User user = new User();
        user.setId(rs.getLong("id"));
        user.setName(rs.getString("name"));
        user.setEmail(rs.getString("email"));
        user.setAge(rs.getInt("age"));
        user.setCreatedAt(rs.getTimestamp("created_at"));
        return user;
    }

    private Connection getConnection() throws SQLException {
        return DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
    }
}

🔧 开发工具与IDE

🎯 IntelliJ IDEA使用技巧

💡 常用快捷键和功能

🎯 IntelliJ IDEA实用技巧

🔧 IntelliJ IDEA 常用快捷键

📝 编辑相关:
├── Ctrl + D          复制当前行
├── Ctrl + Y          删除当前行
├── Ctrl + /          单行注释
├── Ctrl + Shift + /  多行注释
├── Alt + Enter       快速修复
├── Ctrl + Alt + L    格式化代码
└── Ctrl + Alt + O    优化导入

🔍 导航相关:
├── Ctrl + N          查找类
├── Ctrl + Shift + N  查找文件
├── Ctrl + E          最近文件
├── Ctrl + B          跳转到定义
├── Alt + F7          查找使用
└── Ctrl + H          类型层次结构

🏃 运行调试:
├── Shift + F10       运行
├── Shift + F9        调试
├── F8                单步执行
├── F9                继续执行
└── Ctrl + F2         停止

🔧 重构相关:
├── Shift + F6        重命名
├── Ctrl + Alt + M    提取方法
├── Ctrl + Alt + V    提取变量
├── Ctrl + Alt + C    提取常量
└── F6                移动类/方法

🎯 综合实战练习

📊 练习1:构建完整的Web应用 ⭐⭐⭐⭐⭐

📝 练习描述

使用Spring Boot、Maven、JUnit和日志框架构建一个完整的用户管理Web应用,包含CRUD操作、数据验证、异常处理和完整的测试覆盖。

🎯 学习目标
  • 掌握Spring Boot Web开发
  • 熟练使用Maven管理项目
  • 编写完整的单元测试和集成测试
  • 实现合理的日志记录
  • 学会API设计和文档编写
📋 具体要求

基础要求:

  1. 使用Spring Boot创建RESTful API
  2. 实现用户的增删改查功能
  3. 添加数据验证和异常处理
  4. 编写完整的单元测试
  5. 配置合理的日志输出

进阶要求:
6. 添加分页和排序功能
7. 实现数据库事务管理
8. 添加API文档(Swagger)
9. 实现缓存机制
10. 添加性能监控

🧪 练习2:测试驱动开发实践 ⭐⭐⭐⭐

📝 练习描述

采用TDD(测试驱动开发)方式,先编写测试用例,再实现功能代码,构建一个计算器库。

🎯 学习目标
  • 理解TDD开发流程
  • 掌握JUnit 5高级特性
  • 学会使用Mockito进行模拟测试
  • 提高代码质量和测试覆盖率

🔧 练习3:日志分析工具开发 ⭐⭐⭐

📝 练习描述

开发一个日志分析工具,能够解析不同格式的日志文件,统计访问量、错误率等指标。

🎯 学习目标
  • 深入理解日志框架配置
  • 学会文件处理和数据分析
  • 掌握正则表达式使用
  • 实现数据可视化展示

✅ 完成标准

🎯 基础完成标准
  • 能够使用Maven/Gradle管理Java项目
  • 熟练编写JUnit单元测试
  • 正确配置和使用日志框架
  • 掌握Servlet和Spring Boot基础
  • 能够进行基本的数据库操作
🚀 进阶完成标准
  • 能够设计和实现RESTful API
  • 掌握测试驱动开发方法
  • 熟练使用开发工具提高效率
  • 能够进行性能优化和监控
  • 具备完整项目的构建和部署能力
🏆 专家级标准
  • 能够设计企业级应用架构
  • 掌握微服务开发模式
  • 具备DevOps实践能力
  • 能够指导团队技术选型
  • 具备解决复杂技术问题的能力

📝 本章小结

🎯 核心知识点回顾

🔨 构建工具
  • ✅ 掌握Maven项目管理和依赖配置
  • ✅ 了解Gradle构建脚本编写
  • ✅ 熟练使用构建工具命令
  • ✅ 理解项目生命周期管理
🧪 测试框架
  • ✅ 掌握JUnit 5测试编写
  • ✅ 熟练使用Mockito进行模拟测试
  • ✅ 理解测试驱动开发方法
  • ✅ 学会编写高质量测试用例
📝 日志框架
  • ✅ 掌握SLF4J + Logback配置
  • ✅ 理解日志级别和输出格式
  • ✅ 学会性能日志和异常日志记录
  • ✅ 掌握MDC上下文信息使用
🌐 Web开发
  • ✅ 理解Servlet基础概念
  • ✅ 掌握Spring Boot Web开发
  • ✅ 学会RESTful API设计
  • ✅ 掌握HTTP请求处理
🗄️ 数据库操作
  • ✅ 掌握JDBC基础操作
  • ✅ 理解数据库连接管理
  • ✅ 学会SQL语句编写和优化
  • ✅ 了解ORM框架基础
🔧 开发工具
  • ✅ 熟练使用IDE提高开发效率
  • ✅ 掌握调试和重构技巧
  • ✅ 学会版本控制和协作开发
  • ✅ 了解持续集成和部署

📈 学习成果检验

🎯 自我检测清单
  • 能够独立搭建Java Web项目
  • 熟练编写和运行单元测试
  • 正确配置日志输出和分析
  • 掌握数据库连接和操作
  • 能够使用开发工具提高效率
  • 理解企业级开发最佳实践
🚀 进阶学习方向
方向内容推荐资源
🌐 Spring生态Spring Boot、Spring CloudSpring官方文档
🗄️ 数据库技术MyBatis、JPA、Redis数据库设计与优化
🔧 DevOps实践Docker、K8s、CI/CD云原生技术栈
📊 监控运维Prometheus、ELK、APM系统监控与日志分析

🗺️ 专栏学习路径图

📈 Java程序员从0到1成长路径
🎯 第一阶段:Java基础入门 (4-6周) - ✅ 已完成
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ 1.开发环境   │───▶│ 2.基础语法   │───▶│ 3.面向对象   │───▶│ 4.核心API   │
│   搭建      │    │             │    │   编程基础   │    │             │
│ ✅ 已完成    │    │ ✅ 已完成    │    │ ✅ 已完成    │    │ ✅ 已完成    │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘

🚀 第二阶段:Java进阶技能 (4-6周) - ✅ 已完成
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ 1.高级特性   │    │ 2.I/O与网络  │    │ 3.新特性     │    │ 4.工具框架   │
│             │    │   编程      │    │             │    │   入门      │
│ ✅ 已完成    │    │ ✅ 已完成    │    │ ✅ 已完成    │    │ ✅ 已完成    │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘

🎯 第三阶段:项目实战 (4-8周)
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ 1.小型项目   │    │ 2.Web开发    │    │ 3.Spring    │    │ 4.数据库与   │
│   开发      │    │   基础      │    │   框架入门   │    │   持久层    │
│ 🎯 下一步    │    │ 📅 计划中    │    │ 📅 计划中    │    │ 📅 计划中    │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘

🏆 第四阶段:职业发展 (持续更新)
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ 1.代码质量   │    │ 2.开发工具   │    │ 3.面试准备   │    │ 4.职业规划   │
│   与规范    │    │   链        │    │             │    │             │
│ 📅 计划中    │    │ 📅 计划中    │    │ 📅 计划中    │    │ 📅 计划中    │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
📊 第二阶段完整进度
章节主要内容预计时间难度状态
🔧 1.Java高级特性泛型、反射、注解、枚举1-2周⭐⭐⭐⭐✅ 已完成
📁 2.I/O与网络编程文件操作、网络通信、多线程1-2周⭐⭐⭐⭐✅ 已完成
🆕 3.Java新特性Lambda、Stream API、新日期API1周⭐⭐⭐⭐✅ 已完成
🛠️ 4.工具与框架入门Maven/Gradle、JUnit、日志框架1周⭐⭐⭐✅ 已完成
🎉 第二阶段完成!
• 第二阶段进度:100% (4/4章节完成)
• 已掌握:Java高级特性、I/O网络编程、新特性、工具框架
• 下一步:进入第三阶段项目实战学习
• 重点:小型项目开发、Web开发基础、Spring框架入门

🎬 下一章预告

🚀 第三阶段第1章:小型项目开发

🎯 下章学习内容

  • 📋 项目需求分析与设计
  • 🏗️ 项目架构搭建
  • 💻 核心功能实现
  • 🧪 测试与部署实践

💡 学习建议

  • 将所学知识应用到实际项目中
  • 掌握项目开发的完整流程
  • 学会代码组织和模块化设计
  • 建立良好的开发习惯和规范

🎉 恭喜完成Java进阶技能的学习!

你已经掌握了Java开发的进阶技能,
包括高级特性、I/O网络编程、新特性和常用工具框架,
这些技能为你进入项目实战阶段奠定了坚实的基础!

准备好开始你的第一个Java项目了吗? 🚀


📧 有问题?欢迎在评论区讨论交流!
⭐ 觉得有用?别忘了点赞收藏!
🔄 继续关注,项目实战精彩内容即将到来!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值