Spring Data REST
概述
Spring Data REST 是 Spring Data 生态中的「零代码 API 暴露器」——只需定义一个 Repository,就能自动生成完整的 RESTful API 端点,包含分页、排序、关联查询、HATEOAS 链接。
核心价值
- 减少 80% 的 CRUD 代码——内管系统、后台管理场景极其适用
- 自动遵循 REST 规范——GET/POST/PUT/PATCH/DELETE + 状态码
- 内置 HATEOAS——自动生成
_links超媒体链接 - 基于 Spring Data Repository——JPA / MongoDB / Neo4j 等都支持
一、快速开始
1.1 依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>1.2 实体与 Repository
java
@Entity
public class Product {
@Id @GeneratedValue
private Long id;
private String name;
private String category;
private BigDecimal price;
private Boolean published = false;
// getters/setters...
}java
@RepositoryRestResource(path = "products")
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByCategory(@Param("category") String category);
List<Product> findByNameContaining(@Param("keyword") String keyword);
}1.3 启动后自动生成的 API
| 方法 | 端点 | 说明 |
|---|---|---|
GET | /products | 分页查询所有商品 |
GET | /products?category=电子 | 按分类筛选 |
GET | /products?name=苹果&sort=price,desc | 关键词搜索 + 排序 |
GET | /products/1 | 查询单个商品 |
POST | /products | 创建商品 |
PUT | /products/1 | 全量更新 |
PATCH | /products/1 | 部分更新 |
DELETE | /products/1 | 删除商品 |
响应示例:
json
{
"_embedded": {
"products": [
{
"id": 1,
"name": "iPhone 15",
"category": "电子",
"price": 6999.00,
"_links": {
"self": { "href": "http://localhost:8080/products/1" },
"product": { "href": "http://localhost:8080/products/1" }
}
}
]
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}二、高级配置
2.1 全局配置
yaml
spring:
data:
rest:
base-path: /api/v1 # API 基础路径
default-page-size: 20 # 默认每页大小
max-page-size: 1000 # 最大分页大小
return-body-on-create: true # POST 后返回创建的资源
return-body-on-update: true # PUT/PATCH 后返回更新后的资源2.2 @RestResource 细粒度控制
java
@RepositoryRestResource(path = "products", collectionResourceRel = "products")
public interface ProductRepository extends JpaRepository<Product, Long> {
@RestResource(path = "byCategory", rel = "byCategory")
List<Product> findByCategory(@Param("category") String category);
@RestResource(exported = false) // 不暴露此方法
void deleteByCategory(String category);
}2.3 隐藏端点
java
@RepositoryRestResource(exported = false) // 整个 Repository 不暴露
public interface InternalAuditRepository extends JpaRepository<AuditLog, Long> {
}三、事件处理器
Spring Data REST 发布 6 种 ApplicationEvent,可在 CRUD 前后插入业务逻辑:
| 事件 | 说明 |
|---|---|
BeforeCreateEvent | POST 前 |
AfterCreateEvent | POST 后 |
BeforeSaveEvent | PUT/PATCH 前 |
AfterSaveEvent | PUT/PATCH 后 |
BeforeDeleteEvent | DELETE 前 |
AfterDeleteEvent | DELETE 后 |
java
@Component
public class ProductEventHandler {
@HandleBeforeCreate
public void handleBeforeCreate(Product product) {
if (product.getPrice().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("价格必须大于 0");
}
}
@HandleAfterCreate
public void handleAfterCreate(Product product) {
// 发送商品上架通知
notificationService.notifyNewProduct(product);
}
@HandleBeforeDelete
public void handleBeforeDelete(Product product) {
if (product.getPublished()) {
throw new IllegalStateException("已上架商品不能删除,请先下架");
}
}
@HandleBeforeSave
public void handleBeforeSave(Product product) {
product.setUpdateTime(LocalDateTime.now());
}
}四、投影(Projection)
投影允许控制 JSON 输出的字段,避免暴露敏感数据或过度查询。
4.1 定义投影接口
java
@Projection(name = "summary", types = Product.class)
public interface ProductSummary {
String getName();
BigDecimal getPrice();
// 不暴露 id、category、published 等字段
}4.2 使用投影
http
GET /products?projection=summaryjson
{
"name": "iPhone 15",
"price": 6999.00
}4.3 多投影
java
@Projection(name = "detail", types = Product.class)
public interface ProductDetail {
String getName();
String getCategory();
BigDecimal getPrice();
@Value("#{target.price * 0.9}") // SpEL 计算折扣价
BigDecimal getDiscountedPrice();
}五、验证
Spring Data REST 自动集成 Bean Validation:
java
@Entity
public class Product {
@Id @GeneratedValue
private Long id;
@NotBlank(message = "商品名称不能为空")
@Size(max = 128, message = "名称最长 128 字符")
private String name;
@NotNull(message = "价格不能为空")
@DecimalMin(value = "0.01", message = "价格必须大于 0")
private BigDecimal price;
@Pattern(regexp = "^[A-Z]+$", message = "分类编码必须是大写字母")
private String categoryCode;
}验证失败返回:
json
{
"errors": [
{
"entity": "Product",
"message": "商品名称不能为空",
"invalidValue": null,
"property": "name"
}
]
}六、HATEOAS 与自定义资源
6.1 自定义资源处理器
java
@Component
public class ProductResourceProcessor implements RepresentationModelProcessor<EntityModel<Product>> {
@Override
public EntityModel<Product> process(EntityModel<Product> model) {
model.add(Link.of("/api/v1/products/top-rated", "topRated"));
model.add(Link.of("/api/v1/products/promotions", "promotions"));
return model;
}
}6.2 自定义控制器 + Spring Data REST 共存
java
@BasePathAwareController // 在 REST base-path 下注册
public class ProductController {
private final ProductRepository repository;
@GetMapping("/products/search/topRated")
@ResponseBody
public List<Product> topRated() {
return repository.findByCategory("电子");
}
}七、实战:内管系统快速 API
7.1 场景
电商后台管理系统需要快速为运营团队暴露商品、订单、用户等 CRUD API。
7.2 架构
┌─────────────┐ ┌──────────────────┐ ┌──────────┐
│ 运营后台 │────▶│ Spring Data REST │────▶│ 数据库 │
│ (Vue/React) │ │ (自动生成 API) │ │ (MySQL) │
└─────────────┘ └──────────────────┘ └──────────┘7.3 Repository 定义
java
@RepositoryRestResource(path = "orders", collectionResourceRel = "orders")
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUserId(@Param("userId") Long userId);
List<Order> findByStatus(@Param("status") OrderStatus status);
Page<Order> findByCreateTimeBetween(Date start, Date end, Pageable pageable);
}
@RepositoryRestResource(path = "users")
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByPhone(@Param("phone") String phone);
Page<User> findByNameContaining(@Param("name") String name, Pageable pageable);
}7.4 效果对比
| 实现方式 | 代码量 | 开发耗时 |
|---|---|---|
| 传统 Controller + Service | ~200 行 | 1 天 |
| Spring Data REST | 0 行(只需 Repository) | 10 分钟 |
八、总结
| 知识点 | 要点 |
|---|---|
| 核心原理 | 通过 RepositoryRestExporter 将 Repository 自动暴露为 REST 端点 |
| 事件处理器 | @HandleBeforeCreate / @HandleAfterSave / @HandleBeforeDelete 等 |
| 投影 | @Projection 控制输出字段,支持 SpEL 计算 |
| 验证 | 自动集成 JSR-303 Bean Validation |
| HATEOAS | RepresentationModelProcessor 自定义链接 |
| 安全 | 配合 Spring Security 的 @PreAuthorize / @Secured |
| 适用场景 | 内管系统、后台 CRUD、原型快速开发 |
参考链接: