我学习到的Java 的 Service 分层:itf 和 impl 到底是什么?

一、先看目录长什么样

很多 Java 项目(尤其是 Spring Boot 项目)的 Service 层会这么组织:

rust 复制代码
service/
├── itf/
│   └── BannerV2Service.java        // 接口:只声明方法
└── impl/
    └── BannerV2ServiceImpl.java    // 实现类:真正写业务逻辑

这两个文件夹的定位非常清晰:

目录 全称 放什么 例子
itf interface 接口(只声明方法,不写具体逻辑) BannerV2Service.java
impl implementation 实现类(真正写业务代码) BannerV2ServiceImpl.java

关系图大概长这样:

markdown 复制代码
itf/BannerV2Service          ← 约定「能做什么」
        ▲
        │ implements
        │
impl/BannerV2ServiceImpl     ← 具体「怎么做」

二、它解决什么问题?

1. 依赖接口,不依赖实现

Controller 层不直接依赖 impl 里的实现类,而是依赖 itf 里的接口:

kotlin 复制代码
@RestController
public class BannerV2Controller {
    
    @Autowired
    private BannerV2Service bannerV2Service;  // 这是接口,不是实现类
}

这样做的好处是:Controller 只关心"能做什么",不关心"怎么做" 。

比如 bannerV2Service.saveOrUpdateBanner(...),Controller 只知道调用这个方法就能保存 Banner,至于里面是:

  • 直接插入数据库?
  • 先查缓存再写库?
  • 调用别的微服务?

Controller 完全不用管。


2. 一眼分清「有哪些能力」和「怎么实现」

打开 itf/BannerV2Service.java,你看到的是一份干净的 API 清单:

scss 复制代码
public interface BannerV2Service {
    Message saveOrUpdateBanner(BannerV2DTO bannerV2DTO);
    BannerV2DTO getBanner(Long id);
    Page<BannerV2DTO> getBanners(...);
}

而打开 impl/BannerV2ServiceImpl.java,你看到的是具体实现细节:校验、拼参数、调 Mapper、处理异常......

这种拆分让代码更可读、可维护。


3. 方便测试和替换

写单元测试时,你可以轻松 Mock 接口:

scss 复制代码
BannerV2Service mockService = mock(BannerV2Service.class);
when(mockService.getBanner(1L)).thenReturn(mockBanner);

不需要真的去连数据库、启动容器。

如果将来业务需要换一套实现(比如从本地缓存换成 Redis),只需要再写一个 BannerV2RedisServiceImpl,实现同一个接口即可,Controller 一行代码都不用改。


三、和前端"接口"的区别(这个很容易搞混)

很多前端同学看到 interface 会条件反射想到 TypeScript 里的:

csharp 复制代码
interface BannerForm {
  imgUrls: string[];
  gifUrls?: string[];   // 加字段改这里
}

但在 Java 里,itf 里的接口和前端 TS 里的 interface 是两码事:

对比项 前端 TS 的 interface Java 的 Service 接口(itf)
用途 描述数据长什么样(字段) 描述能调用哪些方法
更像 Java 里的 DTO / VO Service 接口
改 gifUrls 时 要加字段 通常不用改

记住一句话:

Java 的 itf ≈ "有哪些 API 方法"

前端的 interface ≈ Java 的 DTO(数据传输对象)

四、一张表总结

角色 路径/文件 改不改 gifUrls 为什么
Service 接口 itf/BannerV2Service.java 不改 方法签名没变,还是传入 BannerV2DTO
Service 实现 impl/BannerV2ServiceImpl.java 要改逻辑 真正处理 gifUrls 的地方
DTO(数据传输对象) BannerV2DTO.java 要加字段 这是定义数据结构的"前端 interface"
Controller BannerV2Controller.java 不改 只依赖接口,不关心字段变化

结论

  • Java中itf只声明方法,impl是具体实现类
  • 前端interface描述数据长什么样(字段),Java中itf描述能调用哪些方法 ,即itf只是对应interface里方法而不包含成员
相关推荐
3095566662 分钟前
股票接口返回 404 与空数组的排查笔记:证书状态与级别参数
java·笔记·python·ios·iphone·股票数据api·股票数据接口
nice_lcj5206 分钟前
MySQL 存储引擎入门:InnoDB 和 MyISAM 到底怎么选
java·数据库·mysql·面试·innodb·存储引擎·myisam
文丑颜不良啊21 分钟前
LeetCode2861~2880题目解析
java·leetcode
装不满的克莱因瓶33 分钟前
设计模式之策略模式核心概念和应用场景
java·后端·设计模式·架构·系统架构·策略模式
晴天的雨.99238 分钟前
【C++】继承篇
java·开发语言·c++
SomeOtherTime43 分钟前
第 1 讲:内存布局与所有权解剖——栈帧、胖指针与“移动/拷贝”在汇编层面的真实面目
java·开发语言·汇编
qq_406981031 小时前
c++ lamda(三)Lambda表达式
java·前端·c++
头发还在的女程序员1 小时前
Java在线租房系统源码解析:Spring Boot+Vue 3+UniApp房源管理与预约看房方案
java·vue.js·spring boot·租房小程序·租房系统·租房软件
FYKJ_20101 小时前
springboot同人周边订单管理系统48955-计算机毕业设计、课程设计
java·javascript·vue.js·spring boot·django·php·课程设计
Flynn Liang1 小时前
LLM 应用安全:Prompt 注入、越权与输入输出护栏
java·人工智能·大模型·langchain4j·llm 安全·prompt 注入·guardrail