💡 一则也许对你有用的小广告 🏆
欢迎飞飞程序员 ,你将获得:专属的实战项目(已更新的所有会员标识的项目都能学习) / 1v1 提问 / Java 学习路线 / PHP 学习路线 / 学习打卡 / 社群讨论
截止目前, 飞飞 正在疯狂爆肝实战项目,后续还会上新更多项目,目标是将所学知识开发成项目并且分享给大家,如知识付费系统, Ai系统, CMS系统,在线商城系统,等等 ,欢迎点击围观
让我通过实际代码来解释 Service 接口和 ServiceImpl 实现类的关系:
📚 Service 接口 vs ServiceImpl 实现类
1️⃣ Service 接口(定义契约)
// AdminDashboardService.java (接口)
public interface AdminDashboardService {
/**
* 获取仪表盘基础统计信息
*/
Response findDashboardStatistics();
/**
* 获取文章发布热点统计信息
*/
Response findDashboardPublishArticleStatistics();
/**
* 获取文章最近一周 PV 访问量统计信息
*/
Response findDashboardPVStatistics();
}
特点:
- ✅ 只定义方法签名(做什么)
- ✅ 不包含实现代码(怎么做)
- ✅ 作为业务功能的契约
2️⃣ ServiceImpl 实现类(具体实现)
// AdminDashboardServiceImpl.java (实现类)
@Service // ← Spring 注解,表示这是一个服务类
@Slf4j
public class AdminDashboardServiceImpl implements AdminDashboardService { // ← 实现接口
@Autowired
private ArticleMapper articleMapper; // 依赖注入
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private TagMapper tagMapper;
@Override // ← 实现接口中的方法
public Response findDashboardStatistics() {
// 具体的业务逻辑实现
Long articleTotalCount = articleMapper.selectCount(...);
Long categoryTotalCount = categoryMapper.selectCount(...);
Long tagTotalCount = tagMapper.selectCount(...);
FindDashboardStatisticsInfoRspVO vo = FindDashboardStatisticsInfoRspVO.builder()
.articleTotalCount(articleTotalCount)
.categoryTotalCount(categoryTotalCount)
.tagTotalCount(tagTotalCount)
.build();
return Response.success(vo);
}
@Override
public Response findDashboardPublishArticleStatistics() {
// 具体实现...
}
@Override
public Response findDashboardPVStatistics() {
// 具体实现...
}
}
特点:
- ✅ 实现接口的所有方法
- ✅ 包含具体的业务逻辑
- ✅ 注入需要的 Mapper、其他 Service 等依赖
- ✅ 标注
@Service 注解,让 Spring 管理
3️⃣ 关系图解
┌────────────────────────────────────────────────┐
│ Controller 层 │
│ │
│ @Autowired │
│ private AdminDashboardService dashboardService;│ ← 注入接口类型
│ │
│ dashboardService.findDashboardStatistics(); │ ← 调用方法
└─────────────────┬──────────────────────────────┘
│ 依赖注入(Spring IoC)
│
↓
┌────────────────────────────────────────────────┐
│ AdminDashboardService 接口 │
│ (定义业务方法,不包含实现) │
│ │
│ + findDashboardStatistics() │
│ + findDashboardPublishArticleStatistics() │
│ + findDashboardPVStatistics() │
└─────────────────┬──────────────────────────────┘
│ implements(实现)
│
↓
┌────────────────────────────────────────────────┐
│ AdminDashboardServiceImpl 实现类 │
│ @Service (Spring Bean) │
│ (包含具体的业务逻辑实现) │
│ │
│ - articleMapper │
│ - categoryMapper │
│ - tagMapper │
│ │
│ + findDashboardStatistics() { 具体实现 } │
│ + findDashboardPublishArticleStatistics() │
│ + findDashboardPVStatistics() │
└────────────────────────────────────────────────┘
4️⃣ 在 Controller 中的使用
@RestController
@RequestMapping("/admin/dashboard")
public class AdminDashboardController {
@Autowired
private AdminDashboardService dashboardService; // ← 注入接口类型,不是实现类!
@PostMapping("/statistics")
public Response getStatistics() {
// Spring 自动注入 AdminDashboardServiceImpl 实例
return dashboardService.findDashboardStatistics(); // ← 调用接口方法
}
}
关键点:
- Controller 依赖接口(
AdminDashboardService)
- Spring 自动注入实现类(
AdminDashboardServiceImpl)
- 这就是面向接口编程的体现
5️⃣ 为什么要这么设计?(重要!)
✅ 原因 1:面向接口编程(解耦)
// ❌ 不好的做法:直接依赖实现类
@Autowired
private AdminDashboardServiceImpl dashboardService; // 紧耦合
// ✅ 好的做法:依赖接口
@Autowired
private AdminDashboardService dashboardService; // 松耦合
好处:
- Controller 只需要知道接口定义的方法
- 不需要关心具体怎么实现
实现类可以随时替换,不影响调用方
-
✅ 原因 2:方便替换实现(灵活性)
// 接口
public interface AdminDashboardService {
Response findDashboardStatistics();
}
// 实现 1:从数据库查询
@Service("databaseService")
public class AdminDashboardServiceImpl implements AdminDashboardService {
public Response findDashboardStatistics() {
// 从 MySQL 查询
}
}
// 实现 2:从缓存查询
@Service("cacheService")
public class AdminDashboardCacheServiceImpl implements AdminDashboardService {
public Response findDashboardStatistics() {
// 从 Redis 查询
}
}
// Controller 可以灵活切换
@Autowired
@Qualifier("databaseService") // 或 "cacheService"
private AdminDashboardService dashboardService;
✅ 原因 3:便于单元测试(可测试性)
// 测试时可以创建 Mock 实现
public class AdminDashboardServiceMock implements AdminDashboardService {
@Override
public Response findDashboardStatistics() {
// 返回测试数据,不访问真实数据库
return Response.success(mockData);
}
}
// 单元测试
@Test
public void testController() {
AdminDashboardService mockService = new AdminDashboardServiceMock();
// 测试 Controller 逻辑...
}
✅ 原因 4:符合设计原则
设计原则
说明
单一职责
接口定义职责,实现类负责具体逻辑
开闭原则
对扩展开放(新增实现类),对修改关闭(不改接口)
依赖倒置
依赖抽象(接口),不依赖具体实现
接口隔离
接口只定义必要的方法,保持简洁
6️⃣ 命名规范
| 类型 | 命名规范 | 示例 |
| 接口 | XxxService | AdminDashboardService |
| 实现类 | XxxServiceImpl | AdminDashboardServiceImpl |
| 位置 | service/ vs service/impl/ | 接口和实现分开存放 |
service/
├── AdminDashboardService.java ← 接口
├── AdminArticleService.java ← 接口
├── AdminWikiService.java ← 接口
└── impl/
├── AdminDashboardServiceImpl.java ← 实现
├── AdminArticleServiceImpl.java ← 实现
└── AdminWikiServiceImpl.java ← 实现
7️⃣ 完整的调用链
用户请求
↓
┌─────────────────────────────────────┐
│ Controller (接收请求) │
│ @Autowired │
│ private AdminDashboardService xxx; │ ← 依赖接口
└───────────────┬─────────────────────┘
↓ Spring 自动注入实现类
┌─────────────────────────────────────┐
│ Service 接口 (定义方法) │
│ AdminDashboardService │
└───────────────┬─────────────────────┘
↓ implements
┌─────────────────────────────────────┐
│ ServiceImpl 实现类 (业务逻辑) │
│ @Service │
│ AdminDashboardServiceImpl │
│ - 调用 Mapper │
│ - 数据转换 │
│ - 业务处理 │
└───────────────┬─────────────────────┘
↓ 调用
┌─────────────────────────────────────┐
│ Mapper (数据访问) │
│ ArticleMapper │
│ CategoryMapper │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ 数据库 (MySQL) │
└─────────────────────────────────────┘
8️⃣ 实际项目中的优势
场景 1:需求变更
// 原来:直接查数据库
public class AdminDashboardServiceImpl {
public Response findDashboardStatistics() {
// 查询数据库
}
}
// 需求变更:先查缓存,缓存没有再查数据库
public class AdminDashboardServiceImpl {
public Response findDashboardStatistics() {
// 1. 先查 Redis
// 2. 没有则查数据库
// 3. 写入 Redis
}
}
// Controller 不需要改动!
场景 2:多实现共存
// 普通用户:查询简化数据
@Service("simpleService")
public class SimpleDashboardServiceImpl implements AdminDashboardService { }
// VIP 用户:查询详细数据
@Service("detailService")
public class DetailDashboardServiceImpl implements AdminDashboardService { }
// 根据用户类型选择不同实现
@Autowired
@Qualifier(userLevel == VIP ? "detailService" : "simpleService")
private AdminDashboardService service;
🎯 总结
Service 接口
- 📋 定义业务方法(做什么)
- 🎯 业务契约,规定行为
- 🔗 解耦工具,降低依赖
ServiceImpl 实现类
- ⚙️ 实现业务逻辑(怎么做)
- 🔨 调用 Mapper,操作数据
- 💼 业务处理,数据转换
为什么这么用?
- ✅ 解耦:Controller 不依赖具体实现
- ✅ 灵活:可以有多个实现
- ✅ 可测试:方便编写单元测试
- ✅ 可维护:修改实现不影响接口
- ✅ 规范:符合企业级项目开发标准
形象比喻:
- Service 接口 = 菜单(告诉你有什么菜)
- ServiceImpl = 厨师(实际做菜的人)
- Controller = 顾客(只看菜单点菜,不管厨师是谁)
这就是 面向接口编程 的精髓!🎉