💡 一则也许对你有用的小广告 🏆

欢迎飞飞程序员   ,你将获得:专属的实战项目(已更新的所有会员标识的项目都能学习) / 1v1 提问 / Java 学习路线 / PHP 学习路线 / 学习打卡 / 社群讨论

  • 正在进行中的项目:《FFBlog知识付费博客项目》 正在持续更新中,基于 Spring Boot 3.x + JDK 21...,点击查看 ;
  • 《从零开发:FFBlog知识付费博客项目(全栈开发)》 演示链接: https://ffblog.ffcxy.com/  ;

截止目前, 飞飞  正在疯狂爆肝实战项目,后续还会上新更多项目,目标是将所学知识开发成项目并且分享给大家,如知识付费系统, 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,操作数据
  • 💼 业务处理,数据转换

为什么这么用?

  1. 解耦:Controller 不依赖具体实现
  2. 灵活:可以有多个实现
  3. 可测试:方便编写单元测试
  4. 可维护:修改实现不影响接口
  5. 规范:符合企业级项目开发标准

形象比喻

  • Service 接口 = 菜单(告诉你有什么菜)
  • ServiceImpl = 厨师(实际做菜的人)
  • Controller = 顾客(只看菜单点菜,不管厨师是谁)

这就是 面向接口编程 的精髓!🎉

上一篇 详细解释一下 Mapper 和 DO 的关系
下一篇 Java Lombok 用法详解

全部评论(0)

头像
😃 😁 😅 😂 😍 😜 😝 🤑 🥵 🥰 😙 😎 😵 😭 😱 😖 🥳 👽 🙈 🤡 😤 💣 💯 💢 ❤️ 👍 👏 👋 👌 🤏 🙏
还没有任何评论哟~