共140行
2026-05-13 17:15:32
接口投影(Interface-based Projection)是Spring Data JPA提供的一种轻量级DTO映射机制,其核心原理如下:
Spring在运行时通过JDK动态代理为接口创建代理对象:
Proxy.newProxyInstance()生成接口的实现类Spring通过方法名与查询结果列名进行智能匹配:
public interface ZyActivityScoreView {
Long getZyActivityId(); // 匹配列名: zy_activity_id, zyActivityId
BigDecimal getUsedScore(); // 匹配列名: used_score, usedScore
}匹配策略:
getZyActivityId() → zy_activity_idgetZyActivityId() → zyActivityIdAS指定别名查询执行流程:
┌─────────────────────────────────────┐
│ 1. 执行原生SQL查询 │
│ 返回: ResultSet / TupleBackedMap │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 2. Spring读取结果集的元数据 │
│ 获取列名和值 │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 3. 创建接口的动态代理对象 │
│ Proxy implements ZyActivityScoreView│
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 4. 方法调用时从Map中取值 │
│ getZyActivityId() → map.get("zyActivityId")│
└─────────────────────────────────────┘
// Spring内部使用的投影工厂
org.springframework.data.projection.ProxyProjectionFactory
// 核心逻辑(简化版)
public class ProjectionFactory {
public <T> T createProjection(Class<T> projectionType, Object source) {
// source 可以是 Map、Entity、Object[] 等
return (T) Proxy.newProxyInstance(
projectionType.getClassLoader(),
new Class<?>[]{projectionType},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 根据方法名从source中提取对应的值
String propertyName = extractPropertyName(method.getName());
return extractValue(source, propertyName);
}
}
);
}
}// SQL查询返回的结果(每行是一个Map)
{
"zyActivityId": 1001,
"usedScore": 99.99
}
// Spring创建的代理对象行为
ZyActivityScoreView view = proxy;
view.getZyActivityId();
// ↑ 触发InvocationHandler
// ↑ 从Map中获取"zyActivityId"或"zy_activity_id"的值
// ↑ 返回 1001
view.getUsedScore();
// ↑ 从Map中获取"usedScore"或"used_score"的值
// ↑ 返回 99.99| 特性 | 接口投影 | 类投影(NEW语法) | 实体映射 |
|---|---|---|---|
| 性能 | ⭐⭐⭐ 高 | ⭐⭐ 中 | ⭐ 低 |
| 灵活性 | ⭐⭐⭐ 高 | ⭐⭐ 中 | ⭐ 低 |
| 类型安全 | ✅ 是 | ✅ 是 | ✅ 是 |
| 需要构造函数 | ❌ 否 | ✅ 是 | ❌ 否 |
| 支持聚合函数 | ✅ 是 | ✅ 是 | ❌ 否 |
| 懒加载支持 | ❌ 否 | ❌ 否 | ✅ 是 |
// ✅ 正确:方法名遵循getter规范
Long getZyActivityId();
// ❌ 错误:不是getter方法
Long zyActivityId();
// ✅ 支持嵌套投影
interface OrderView {
Long getId();
CustomerView getCustomer(); // 嵌套投影
}
// ✅ 支持SpEL表达式(仅适用于实体投影)
@Value("#{target.firstName + ' ' + target.lastName}")
String getFullName();接口投影的本质是:Spring通过动态代理 + 反射 + 命名约定,将查询结果的列值自动绑定到接口方法的返回值上,避免了手动编写DTO类和转换代码,是一种简洁高效的查询结果映射方式。