JSON 处理库
处理 JSON 是现代后端和前端交互的核心。主要有两个选择:Gson(Google)和 Jackson(更强大,社区更活跃)
a) Gson (Google)
Gson 非常易于使用,适合简单的序列化(Java对象 -> JSON)和反序列化(JSON -> Java对象)。
示例:
import com.google.gson.Gson;
import com.google.gson.JsonObject;
// 1. 简单的对象转换
class User {
private String name;
private int age;
// ... getters and setters
}
User user = new User("Alice", 30);
Gson gson = new Gson();
// 序列化 to JSON
String json = gson.toJson(user);
System.out.println(json); // 输出: {"name":"Alice","age":30}
// 反序列化 from JSON
String inputJson = "{\"name\":\"Bob\",\"age\":25}";
User userObject = gson.fromJson(inputJson, User.class);
System.out.println(userObject.getName()); // 输出: Bob
// 2. 使用 JsonObject 动态构建JSON(你问题中提到的JsonObject)
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("name", "Charlie");
jsonObject.addProperty("age", 28);
jsonObject.addProperty("isStudent", false);
String dynamicJson = gson.toJson(jsonObject);
System.out.println(dynamicJson); // 输出: {"name":"Charlie","age":28,"isStudent":false}
b) Jackson
Jackson 是 Spring 框架默认集成的库,功能更强大,支持流式API、注解等,性能也通常更好。
示例:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
ObjectMapper mapper = new ObjectMapper();
User user = new User("David", 40);
// 序列化
String jsonString = mapper.writeValueAsString(user);
System.out.println(jsonString);
// 反序列化
String jsonInput = "{\"name\":\"Eva\",\"age\":35}";
User parsedUser = mapper.readValue(jsonInput, User.class);
System.out.println(parsedUser.getName());
2. 集合、IO、字符串等工具库
Apache Commons Lang & Commons Collections
这个库提供了大量工具方法,弥补了 Java 标准库的不足,尤其是 StringUtils
, CollectionUtils
等,能让你写出更简洁、健壮的代码。
示例:
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
import java.util.Arrays;
import java.util.List;
// 字符串操作
String str = " hello ";
System.out.println(StringUtils.isEmpty(str)); // false
System.out.println(StringUtils.trim(str)); // "hello"
System.out.println(StringUtils.abbreviate("This is a long string", 10)); // "This is..."
// 集合操作
List<String> list1 = Arrays.asList("a", "b", "c");
List<String> list2 = Arrays.asList("b", "c", "d");
// 取交集
System.out.println(CollectionUtils.intersection(list1, list2)); // [b, c]
// 判断集合是否为空(安全,避免NPE)
System.out.println(CollectionUtils.isEmpty(null)); // true
Guava (Google)
Google 提供的另一个核心库,功能与 Apache Commons 有重叠,但设计哲学不同,提供了更多不可变集合、函数式编程支持、缓存等高级工具。
示例:
import com.google.common.collect.ImmutableList;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
// 1. 创建不可变集合(线程安全,性能好)
ImmutableList<String> immutableList = ImmutableList.of("a", "b", "c");
// immutableList.add("d"); // 这行会抛出 UnsupportedOperationException
// 2. 强大的字符串连接和分割
Joiner joiner = Joiner.on("; ").skipNulls();
String result = joiner.join("Harry", null, "Ron", "Hermione");
System.out.println(result); // "Harry; Ron; Hermione"
// 比 String.split 更灵活
Iterable<String> split = Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.split("foo, bar,, qux");
split.forEach(System.out::println); // 输出 "foo", "bar", "qux"
3. 日期时间库
Java 8+ java.time
(首选)
Java 8 自带的新日期时间 API (java.time
包) 已经非常完善,完全可以替代旧的 Date
和 Calendar
。除非维护老项目,否则应首选这个。
示例:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
// 获取当前日期和时间
LocalDate today = LocalDate.now();
LocalDateTime now = LocalDateTime.now();
// 构造特定日期
LocalDate birthday = LocalDate.of(1995, 5, 23); // 注意月份是 1-12
// 日期计算
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
LocalDate yearsAgo = today.minusYears(30);
// 日期格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = now.format(formatter);
System.out.println(formattedDateTime); // 输出: 2023-10-27 14:30:00
// 解析字符串为日期
LocalDateTime parsedDate = LocalDateTime.parse("2023-10-27 14:30:00", formatter);
Joda-Time (历史选择)
在 Java 8 之前,Joda-Time 是处理日期时间的事实标准。现在官方推荐迁移到 java.time
,因为它的设计很大程度上被 Java 8 采纳了。
4. 单元测试库
JUnit 5
现代Java单元测试的标准框架。
示例:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class MyTest {
@Test
void testAddition() {
assertEquals(4, 2 + 2, "2+2 should be 4");
assertTrue("Hello".startsWith("H"));
}
}
AssertJ
提供流式断言,让测试代码更流畅、易读。
示例:
import static org.assertj.core.api.Assertions.assertThat;
@Test
void testWithAssertJ() {
String name = "Michael";
assertThat(name)
.isNotBlank()
.startsWith("Mi")
.endsWith("ael")
.hasSize(7);
List<String> names = Arrays.asList("John", "Jane", "Michael");
assertThat(names)
.hasSize(3)
.contains("Jane")
.doesNotContain("Bob");
}
5. 日志库
Java 日志体系通常使用 SLF4J 作为门面(接口),Logback 或 Log4j 2 作为具体实现。这种组合避免了代码和具体日志实现库的耦合。
示例:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyClass {
// 获取Logger对象,通常一个类一个
private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);
public void doSomething() {
LOGGER.debug("This is a debug message.");
LOGGER.info("Method doSomething is called.");
try {
// ... some code
} catch (Exception e) {
LOGGER.error("An error occurred while doing something", e);
}
}
}
总结与推荐
类别 | 首选推荐 | 备选/历史选择 | 核心特点 |
---|---|---|---|
JSON | Jackson | Gson | 功能强大,Spring默认集成 |
工具类 | Apache Commons Lang, Guava | - | 填补JDK空白,提供高效工具方法 |
日期时间 | Java 8 java.time |
Joda-Time | 官方现代API,线程安全,设计优良 |
测试 | JUnit 5 + AssertJ | JUnit 4 | 流式断言,表达力强 |
日志 | SLF4J + Logback | Log4j 2 | 门面模式,解耦,灵活配置 |
给你的建议:
- 1.新项目:直接使用 Jackson、Java 8 time、JUnit 5、SLF4J + Logback 和 Guava/Apache Commons 的组合,这是目前最主流、最现代的技术栈。
- 2.老项目维护:了解项目正在使用的库(可能是 Gson、Joda-Time、JUnit 4),保持一致性,除非你有计划且有能力进行升级重构。
1. 数据库连接池
在 Web 应用中,直接为每个请求创建新的数据库连接开销巨大。连接池负责维护和管理一组数据库连接,使用时从池中获取,用完后归还,极大地提升了性能。
HikariCP
它是目前速度最快、最轻量的连接池,是 Spring Boot 2.x 以后的默认连接池。
示例 (Spring Boot 配置和使用):
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: user
password: pass
hikari:
maximum-pool-size: 10 # 最大连接数
connection-timeout: 30000 # 连接超时时间(ms)
在你的代码中,你无需直接操作 HikariCP,而是通过 DataSource
接口使用它,Spring Boot 会自动注入。
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@RestController
public class MyController {
@Autowired
private DataSource dataSource; // 由 Spring 管理的 HikariCP 数据源
public void doDatabaseOperation() {
// 从连接池获取连接
try (Connection connection = dataSource.getConnection()) {
// 使用 connection 进行数据库操作...
// ...
} catch (SQLException e) {
e.printStackTrace();
}
// try-with-resources 语句会自动将 connection.close(),即归还给连接池
}
}
2. 映射工具 (Object Mapping)
手动在数据库查询结果 (ResultSet
) 和 Java 对象之间进行转换非常繁琐。映射工具可以自动完成这项工作。
MapStruct
它是一个基于注解的代码生成器,在编译期生成映射代码,因此性能极高,等同于手写代码。
示例:
定义两个类和一个映射接口。
// 源对象
public class CarDto {
private String make;
private int numberOfSeats;
// ... getters and setters
}
// 目标对象
public class Car {
private String manufacturer;
private int seatCount;
// ... getters and setters
}
定义映射器接口,使用 @Mapper
注解。
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper // 编译后,MapStruct 会生成此接口的实现类,如 CarMapperImpl
public interface CarMapper {
@Mapping(source = "make", target = "manufacturer")
@Mapping(source = "numberOfSeats", target = "seatCount")
Car carDtoToCar(CarDto carDto);
}
使用生成的映射器:
// 假设通过依赖注入获取 mapper 实例
CarDto dto = new CarDto("Toyota", 5);
Car car = mapper.carDtoToCar(dto);
// car.getManufacturer() == "Toyota"
// car.getSeatCount() == 5
3. Excel 操作库
在企业应用中,经常需要导入/导出 Excel 文件。
Apache POI
功能最强大、最底层的 Java API,可以操作 .xls 和 .xlsx 格式的 Excel 文件。
示例:创建一个简单的 Excel 文件 (.xlsx):
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ExcelExample {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook(); // 创建新工作簿
Sheet sheet = workbook.createSheet("Employee Data"); // 创建工作表
// 创建标题行
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("Name");
headerRow.createCell(1).setCellValue("Age");
// 创建数据行
Row dataRow = sheet.createRow(1);
dataRow.createCell(0).setCellValue("John Doe");
dataRow.createCell(1).setCellValue(30);
// 将工作簿写入文件
try (FileOutputStream fileOut = new FileOutputStream("workbook.xlsx")) {
workbook.write(fileOut);
}
workbook.close();
}
}
EasyExcel (阿里开源)
针对 POI 的优化版,解决了 POI 耗内存的问题(通过逐行解析),API 更简单,特别适合处理大文件。
示例:
// 写入Excel
EasyExcel.write("demo.xlsx", DemoData.class).sheet("模板").doWrite(dataList);
// 读取Excel(监听器模式,逐行读取,不耗内存)
EasyExcel.read("demo.xlsx", DemoData.class, new AnalysisEventListener<DemoData>() {
@Override
public void invoke(DemoData data, AnalysisContext context) {
// 每读一行数据,都会调用此方法
System.out.println("读取到一条数据: " + data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 全部读取完成
}
}).sheet().doRead();
4. HTTP 客户端
用于在 Java 代码中发送 HTTP 请求,调用其他服务的 API。
OkHttp
一个高效的 HTTP & HTTP/2 客户端,API 简洁明了。
示例:发送一个 GET 请求
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class OkHttpExample {
public static void main(String[] args) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://api.github.com/users/google")
.build();
// 同步调用
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
String responseBody = response.body().string();
System.out.println(responseBody);
}
}
}
}
5. 缓存库
在内存中缓存常用数据,避免频繁访问数据库,极大提升应用性能。
Caffeine
一个高性能、高命中率的缓存库,是 Guava Cache 的现代版替代品。
示例:
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
public class CaffeineExample {
public static void main(String[] args) {
// 构建缓存
Cache<String, Object> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入10分钟后过期
.maximumSize(10_000) // 最大缓存条目数
.build();
// 1. 手动操作
String key = "user:1001";
// 存入缓存
cache.put(key, new Object());
// 从缓存获取,如果不存在则返回 null
Object value = cache.getIfPresent(key);
// 2. 自动操作(推荐):如果缓存不存在,则通过Lambda生成值并存入
Object value2 = cache.get(key, k -> createExpensiveValue(k));
System.out.println(value2);
}
private static Object createExpensiveValue(String key) {
// 模拟一个耗时/耗资源的操作(如查数据库)
return "Data for " + key;
}
}
6. 简化代码的利器
Lombok
通过注解在编译时自动生成 Getter、Setter、构造函数、toString()
等样板代码,让代码极其简洁。
示例:
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@Data // 自动生成 getter, setter, toString, equals, hashCode
@AllArgsConstructor // 全参构造器
@NoArgsConstructor // 无参构造器
public class User {
private Long id;
private String name;
private Integer age;
}
// 使用这个类,你无需写任何 get/set 方法!
public class Main {
public static void main(String[] args) {
User user = new User(1L, "Alice", 30);
System.out.println(user.getName()); // 可以直接调用,Lombok在编译时生成了getName()
System.out.println(user.toString()); // 输出: User(id=1, name=Alice, age=30)
}
}
总结表格
类别 | 推荐库 | 核心特点 | 适用场景 |
---|---|---|---|
数据库连接池 | HikariCP | 极快、轻量 | 所有需要数据库连接的 Web 应用 |
对象映射 | MapStruct | 编译期生成,性能无敌 | DTO、VO、DO 等对象间转换 |
Excel 操作 | Apache POI (全功能) / EasyExcel (大文件) | POI 功能全,EasyExcel 省内存 | 报表生成、数据导入导出 |
HTTP 客户端 | OkHttp | API 现代、高效 | 调用第三方 API、微服务间通信 |
缓存 | Caffeine | 高性能、高命中率 | 缓存热点数据,提升访问速度 |
代码简化 | Lombok | 通过注解减少样板代码 | 所有需要定义 POJO 的项目 |
Spring Boot项目结构
一个典型的 Spring Boot 项目结构遵循约定大于配置的原则
flowchart TD
User[用户/客户端]
subgraph SpringBootApp [Spring Boot 应用]
C[Controller
控制层]
S[Service
业务逻辑层]
R[Repository
数据访问层]
C -- 调用 --> S
S -- 调用 --> R
end
Database[(数据库)]
User -- HTTP请求 --> C
C -- HTTP响应 --> User
R -- JDBC操作 --> Database
Database -- 返回数据 --> R
src/
├── main/
│ ├── java/ # 源代码根目录
│ │ └── com/
│ │ └── example/
│ │ └── yourapp/ # 你的项目基包,通常是公司域名反写
│ │ ├── Application.java # 主启动类(核心)
│ │ ├── controller/ # 控制层(Web层)
│ │ ├── service/ # 业务逻辑层(服务层)
│ │ │ ├── impl/ # 服务实现类(可选)
│ │ ├── repository/ # 数据访问层(Repository层)
│ │ ├── entity/ # 实体类(模型层)
│ │ ├── dto/ # 数据传输对象(Data Transfer Object)
│ │ ├── config/ # 配置类
│ │ └── exception/ # 自定义异常处理
│ └── resources/ # 资源文件根目录
│ ├── static/ # 静态资源(js, css, images等)
│ ├── templates/ # 模板文件(Thymeleaf, Freemarker等)
│ ├── application.properties # 主配置文件(或application.yml)
│ └── ...
├── test/
│ └── java/ # 测试代码目录(结构通常与main/java镜像)
└── pom.xml # Maven项目配置文件
1.主启动类 (Application.java
)
位置: 位于项目基包的根目录下。
职责: 程序的入口点。包含 main
方法,通过 @SpringBootApplication
注解启动整个Spring Boot应用。
示例:
package com.example.yourapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // 核心注解,开启了组件扫描和自动配置
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args); // 启动嵌入式的Tomcat和应用
}
}
2. 控制层 (Controller)
位置: com.example.yourapp.controller
职责:接收用户的HTTP请求(GET, POST, PUT, DELETE等)。
调用Service层处理业务。
将处理结果封装成 JSON/XML 返回给前端(RESTful API)或者选择视图进行渲染(传统MVC)。
注解: @RestController
(用于API), @Controller
(用于MVC), @RequestMapping
, @GetMapping
, @PostMapping
等。
示例 (一个RESTful API Controller):
package com.example.yourapp.controller;
import com.example.yourapp.entity.User;
import com.example.yourapp.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController // 表明这个类的所有方法返回的数据直接写入响应体,而不是视图名
@RequestMapping("/api/users") // 父路径
public class UserController {
@Autowired // 依赖注入,将UserService注入进来
private UserService userService;
@GetMapping // 处理 GET /api/users 请求
public List<User> getAllUsers() {
return userService.findAllUsers(); // 调用服务层方法
}
@GetMapping("/{id}") // 处理 GET /api/users/1 请求
public User getUserById(@PathVariable Long id) { // @PathVariable 获取URL中的变量
return userService.findUserById(id);
}
@PostMapping // 处理 POST /api/users 请求
public User createUser(@RequestBody User user) { // @RequestBody 解析请求体中的JSON
return userService.createUser(user);
}
}
3. 业务逻辑层 (Service)
- •位置:
com.example.yourapp.service
- •职责:包含应用的核心业务逻辑和计算规则。
- •处理事务管理(通常使用
@Transactional
注解)。 - •调用Repository层来获取或持久化数据。
- •这一层是解耦的关键,Controller 不应该直接访问 Repository。
- •处理事务管理(通常使用
- •注解:
@Service
,@Transactional
。 - 示例:
package com.example.yourapp.service;
import com.example.yourapp.entity.User;
import com.example.yourapp.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service // 标记为Spring管理的业务逻辑组件
public class UserService {
@Autowired
private UserRepository userRepository;
// 业务方法:获取所有用户
public List<User> findAllUsers() {
return userRepository.findAll(); // 直接调用Repository
}
// 业务方法:创建用户,并开启事务
@Transactional // 此方法会在一个数据库事务中执行
public User createUser(User user) {
// 这里可以添加业务逻辑,例如:检查用户名是否已存在
if (userRepository.existsByUsername(user.getUsername())) {
throw new RuntimeException("Username already exists!");
}
// ... 其他业务逻辑,如密码加密等
return userRepository.save(user); // 保存到数据库
}
public User findUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
}
4. 数据访问层 (Repository / Dao)
- •位置:
com.example.yourapp.repository
- •职责:直接与数据库交互,执行CRUD(增删改查)操作。
- •这是最数据库相关的部分。
- •通常通过继承 Spring Data JPA 的接口来获得大部分通用方法,无需编写实现。
- •注解:
@Repository
(可省略,因为JpaRepository已经包含了)。 - •示例:
package com.example.yourapp.repository;
import com.example.yourapp.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository // 标记为数据访问组件
public interface UserRepository extends JpaRepository<User, Long> {
// JpaRepository<User, Long> 提供了 save(), findById(), findAll(), deleteById() 等基本方法
// 根据方法名自动生成查询(Spring Data JPA 的强大功能)
Optional<User> findByUsername(String username);
Boolean existsByUsername(String username);
// 你也可以使用 @Query 注解来自定义JPQL或原生SQL查询
// @Query("SELECT u FROM User u WHERE u.email = :email")
// Optional<User> findByEmailAddress(@Param("email") String email);
}
5. 实体类 (Entity / Model)
•位置:
com.example.yourapp.entity
或model
•职责:
映射数据库中的表结构。每个实体类实例代表数据库中的一行记录。
这是高度数据库相关的模型定义。
•注解:
@Entity
,@Table
,@Id
,@GeneratedValue
,@Column
等 (JPA注解)。•示例:
package com.example.yourapp.entity;
import javax.persistence.*;
@Entity // 告诉JPA这是一个实体类,将会被映射到数据库表
@Table(name = "users") // 指定映射的表名,如果类名和表名一致可省略
public class User {
@Id // 标记为主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增策略
private Long id;
@Column(nullable = false, unique = true, length = 50) // 映射字段,非空,唯一,长度50
private String username;
@Column(nullable = false)
private String password;
@Column(name = "email_address") // 映射到数据库中的 email_address 列
private String email;
// 必须有无参构造函数
public User() {
}
// 通常会有全参构造函数、Getter和Setter方法
// ... (通常使用Lombok的 @Data 注解来省略手写)
}
6. 资源配置文件
•位置:
src/main/resources/
•职责:
application.properties
/application.yml
: 核心配置文件,配置数据库连接、服务器端口、日志级别、第三方密钥等。static/
: 存放静态资源,如.css
,.js
, 图片等。访问时无需经过Controller,如http://localhost:8080/css/style.css
。templates/
: 存放服务器端模板文件,如.html
(由Thymeleaf或Freemarker渲染)。
示例 (application.yml):
# 配置数据源 (数据库相关)
spring:
datasource:
url: jdbc:mysql://localhost:3306/my_database?useSSL=false&serverTimezone=UTC
username: root
password: mypassword
driver-class-name: com.mysql.cj.jdbc.Driver
# 配置JPA (数据库相关)
jpa:
hibernate:
ddl-auto: update # 启动时根据实体类自动更新表结构(生产环境慎用)
show-sql: true # 在控制台显示执行的SQL语句
# 配置服务器
server:
port: 8080
# 自定义配置
myapp:
upload-path: /var/uploads/
总结与关系梳理
组件/层 | 包位置 | 核心职责 | 相关技术注解 |
---|---|---|---|
启动类 | 基包根目录 | 应用入口,启动Spring容器 | @SpringBootApplication |
控制层 | .controller |
处理HTTP请求/响应,桥梁 | @RestController , @GetMapping |
业务逻辑层 | .service |
实现核心业务逻辑,事务管理 | @Service , @Transactional |
数据访问层 | .repository |
与数据库交互,CRUD操作 | @Repository (常省略), JPA接口 |
实体类 | .entity |
映射数据库表,数据模型 | @Entity , @Id , @Column |
配置文件 | resources/ |
配置应用参数,数据库连接等 | application.properties/yml |
数据流:
- 1.请求进入:客户端 ->
Controller
- 2.逻辑处理:
Controller
->Service
- 3.数据存取:
Service
->Repository
-> 数据库 - 4.响应返回:数据库 ->
Repository
->Service
->Controller
-> 客户端
这种分层架构确保了代码的高内聚、低耦合,每一层职责单一,便于开发、测试和维护