🚀 第二阶段: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 对比总结
| 特性 | Maven | Gradle |
|---|---|---|
| 配置文件 | XML (pom.xml) | Groovy/Kotlin DSL (build.gradle) |
| 学习曲线 | 相对简单,XML配置直观 | 稍陡峭,但更灵活 |
| 构建速度 | 较慢,特别是大项目 | 更快,支持增量构建和并行执行 |
| 依赖管理 | 成熟稳定,传递依赖处理好 | 灵活强大,版本冲突解决更智能 |
| 插件生态 | 非常丰富,历史悠久 | 快速发展,现代化插件 |
| 企业采用 | 广泛使用,企业首选 | 快速增长,特别是新项目 |
| IDE支持 | 优秀,所有主流IDE都支持 | 优秀,现代IDE都有良好支持 |
| 多模块项目 | 支持良好 | 支持优秀,配置更简洁 |
| 自定义任务 | 通过插件,相对复杂 | 非常灵活,易于自定义 |
| 构建缓存 | 基础支持 | 强大的构建缓存机制 |
🎯 选择建议
选择 Maven 的场景:
- 团队对 XML 配置熟悉
- 企业环境,需要稳定性
- 项目相对简单,标准化程度高
- 需要与现有 Maven 生态集成
选择 Gradle 的场景:
- 追求构建性能和效率
- 项目复杂,需要灵活的构建逻辑
- 团队技术水平较高
- Android 开发(Gradle 是官方构建工具)
- 微服务架构,多模块项目
💡 最佳实践建议
- 新手建议:从 Maven 开始学习,掌握构建工具的基本概念
- 项目选择:根据团队技术栈和项目需求选择
- 混合使用:可以在不同项目中使用不同工具
- 持续学习:两种工具都在不断发展,保持学习
🧪 单元测试框架
🎯 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设计和文档编写
📋 具体要求
基础要求:
- 使用Spring Boot创建RESTful API
- 实现用户的增删改查功能
- 添加数据验证和异常处理
- 编写完整的单元测试
- 配置合理的日志输出
进阶要求:
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 Cloud | Spring官方文档 |
| 🗄️ 数据库技术 | 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、新日期API | 1周 | ⭐⭐⭐⭐ | ✅ 已完成 |
| 🛠️ 4.工具与框架入门 | Maven/Gradle、JUnit、日志框架 | 1周 | ⭐⭐⭐ | ✅ 已完成 |
• 第二阶段进度:100% (4/4章节完成)
• 已掌握:Java高级特性、I/O网络编程、新特性、工具框架
• 下一步:进入第三阶段项目实战学习
• 重点:小型项目开发、Web开发基础、Spring框架入门
🎬 下一章预告
🚀 第三阶段第1章:小型项目开发
🎯 下章学习内容
- 📋 项目需求分析与设计
- 🏗️ 项目架构搭建
- 💻 核心功能实现
- 🧪 测试与部署实践
💡 学习建议
- 将所学知识应用到实际项目中
- 掌握项目开发的完整流程
- 学会代码组织和模块化设计
- 建立良好的开发习惯和规范
🎉 恭喜完成Java进阶技能的学习!
你已经掌握了Java开发的进阶技能,
包括高级特性、I/O网络编程、新特性和常用工具框架,
这些技能为你进入项目实战阶段奠定了坚实的基础!
准备好开始你的第一个Java项目了吗? 🚀
📧 有问题?欢迎在评论区讨论交流!
⭐ 觉得有用?别忘了点赞收藏!
🔄 继续关注,项目实战精彩内容即将到来!
365

被折叠的 条评论
为什么被折叠?



