- 理解 Service 层的作用和意义
- 编写 Service 层代码,处理 “打招呼” 的业务逻辑
- 在 Controller 中调用 Service 层,完成完整的请求处理流程
- 理解 “分层开发” 的核心思想 —— 把接口的请求接收和业务逻辑分开,这是企业开发的标准做法。
核心思想先讲透
| Controller | 接收前端请求、返回响应(“前台接待”) | 餐厅的服务员,只负责接单、送菜 |
| Service | 处理核心业务逻辑(“后厨加工”) | 餐厅的厨师,负责做菜的核心流程 |
简单说:Controller 只做 “传话筒”,复杂的逻辑都交给 Service 处理,这样代码更易维护、易复用。
第一步:创建 Service 层
文件层级:

1.1 创建 Service 包(遵循分层规范)
package com.example.demo.service;
// 业务接口:定义“打招呼”的业务规范
public interface HelloService {
// 无参数打招呼
String sayHi();
// 给用户打招呼(带参数)
String sayHiToUser(String name);
// 给客户打招呼(带参数)
String sayHiToCustomer(String name);
}
1.2 创建 Service 接口
企业开发中,通常先写接口再写实现类,便于后续扩展(比如多套业务逻辑):
package com.example.demo.service;
import org.springframework.stereotype.Service;
// ★ 核心注解:@Service
// 作用:告诉 Spring “这是一个业务层组件”,会被自动管理(放入 Spring 容器)
@Service
public class HelloServiceImpl implements HelloService {
// 实现“无参数打招呼”的业务逻辑
@Override
public String sayHi() {
// 这里可以写复杂逻辑:比如拼接时间、调用其他服务、计算等
return "你好!这是 Service 层处理的响应 🎯";
}
// 实现“给用户打招呼”的业务逻辑
@Override
public String sayHiToUser(String name) {
// 业务逻辑1:空值处理
if (name == null || name.isEmpty()) {
name = "陌生人";
}
// 业务逻辑2:模拟复杂处理(比如拼接业务标识)
String businessId = "USER_" + System.currentTimeMillis();
return "【" + businessId + "】你好," + name + "!Service 层已处理你的请求";
}
// 实现“给客户打招呼”的业务逻辑
@Override
public String sayHiToCustomer(String name) {
// 业务逻辑:空值处理 + 客户专属文案
if (name == null || name.isEmpty()) {
name = "陌生的客人";
}
return "【客户专属】Hello," + name + "!你的请求已由 Service 层处理完成";
}
}
第二步:修改 Controller 层(调用 Service)
把 Controller 里的业务逻辑删掉,改为调用 Service 层,让 Controller 只做 “请求转发”:
package com.example.demo.controller;
import com.example.demo.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/hello")
public class HelloController {
// ★ 核心步骤1:注入 Service 组件
// @Autowired:Spring 自动从容器中找到 HelloService 实现类,赋值给这个变量
@Autowired
private HelloService helloService;
// 调用 Service 处理无参数请求
@GetMapping("/hi")
public String sayHi() {
// 不再写业务逻辑,直接调用 Service
return helloService.sayHi();
}
// 调用 Service 处理用户请求
@GetMapping("/user")
public String sayHiToUser(String name) {
return helloService.sayHiToUser(name);
}
// 调用 Service 处理客户请求
@GetMapping("/customer")
public String sayHiToCustomer(String name) {
return helloService.sayHiToCustomer(name);
}
}
核心代码解释
第三步:测试运行(验证 Service 层是否生效)
3.1 重启项目
修改代码后,必须重启 DemoApplication(点击 IDEA 顶部的重启按钮)。
3.2 测试接口(查看 Service 处理结果)
| localhost:8081/demo/hello/hi | 你好!这是 Service 层处理的响应 🎯 |
| localhost:8081/demo/hello/user?name=小明 | 【USER_1740123456789】你好,小明!Service 层已处理你的请求 |
| localhost:8081/demo/hello/customer?name=李总 | 【客户专属】Hello,李总!你的请求已由 Service 层处理完成 |
tips:进阶知识点(可选,企业开发常用)
1. 为什么要写 Service 接口?
- 便于扩展:比如后续要加 “VIP 客户打招呼逻辑”,只需新建 VipHelloServiceImpl 实现 HelloService,不用改 Controller 代码;
- 便于测试:单元测试时可以用 Mock 模拟 Service 接口,不依赖真实逻辑。
2. 依赖注入的另一种写法(更规范)
// 推荐写法:构造器注入(避免空指针,更易测试)
@RestController
@RequestMapping("/hello")
public class HelloController {
private final HelloService helloService;
// 通过构造器注入,替代 @Autowired 字段注入
public HelloController(HelloService helloService) {
this.helloService = helloService;
}
// 接口方法不变…
}
在service包中为什么要用interface 而不用class
public interface HelloService {
// 无参数打招呼
String sayHi();
// 给用户打招呼(带参数)
String sayHiToUser(String name);
// 给客户打招呼(带参数)
String sayHiToCustomer(String name);
}为什么这里是interface而不是class
这里用 interface(接口)而不是 class(类),本质是为了遵循面向接口编程的设计思想,这是 Java 开发(尤其是 Spring 生态)中最核心的最佳实践之一。用 “生活化例子 + 代码对比” 的方式,让你彻底理解背后的原因。
先用生活化例子理解:接口 = “规范 / 合同”
假设你开了一家餐厅,需要招聘 “厨师”:
- 接口(interface):就是 “厨师岗位规范”—— 规定厨师必须会做 3 件事:做主食、做热菜、做汤(只定义 “要做什么”,不定义 “怎么做”);
- 类(class):就是具体的厨师 —— 比如 “川菜厨师”“粤菜厨师”,他们都遵守 “厨师规范”,但做同一道菜的方法完全不同(实现 “怎么做”)。
对应到我们的代码:
- HelloService 接口:定义了 “打招呼业务” 的规范(要实现 sayHi、sayHiToUser、sayHiToCustomer 三个方法);
- HelloServiceImpl 类:是这个规范的具体实现(定义每个方法具体返回什么内容)。
为什么不用 class 直接写?(对比说明)
如果直接写 class HelloService,虽然能实现功能,但会有两个致命问题:
问题 1:代码耦合度高,无法灵活替换逻辑
假设你后续想加 “VIP 客户专属打招呼逻辑”:
- 用 interface:只需新建 VipHelloServiceImpl 实现 HelloService,Controller 完全不用改(因为 Controller 依赖的是 HelloService 接口,不是具体的实现类);
// 新增VIP实现类,不改动原有代码
@Service("vipHelloService")
public class VipHelloServiceImpl implements HelloService {
@Override
public String sayHi() { return "VIP专属问候!"; }
@Override
public String sayHiToUser(String name) { return "VIP你好," + name; }
@Override
public String sayHiToCustomer(String name) { return "VIP客户你好," + name; }
}
- 用 class:必须修改原有 HelloService 类的代码,容易引入 bug,也违反 “开闭原则”(对扩展开放,对修改关闭)。
问题 2:无法实现多态,不利于测试和扩展
Java 是单继承的(一个类只能继承一个父类),但可以实现多个接口。如果 HelloService 是 class,后续想让它同时遵守 “日志规范”“权限规范” 就会受限;而接口可以让一个类同时实现多个规范,比如:
// 一个实现类可以同时遵守多个接口规范
public class HelloServiceImpl implements HelloService, LogService, AuthService {
// 实现所有接口的方法
}
问题 3:不符合 Spring 生态的最佳实践
Spring 框架的核心(依赖注入、AOP、事务管理)都是基于接口设计的:
- Controller 中注入的是 HelloService 接口,Spring 可以灵活切换不同的实现类(比如开发环境用测试实现,生产环境用正式实现);
- 如果直接用 class,Controller 会和具体实现强绑定,失去 Spring 依赖注入的灵活性。




