Spring IOC/DI 与 MVC 从入门到实战
目录
🎯首先学习Spring IOC/DI
1、Spring IOC/DI整体框架
1.1 核心思想:
1.2 两个关键字:
1.3 对象之间的依赖关系
2、真实案例(对比传统java和Spring(IOC和DI)):
2.1 ❌ 场景一:传统java没有Spring——“自己动手,丰衣足食”
2.1.1、定义一个服务类
2.1.2、使用方法
2.2 ✅ 场景二:使用 Spring(IOC + DI)—— “点菜式开发”
2.2.1、定义服务类(加注解)
2.2.2 、启动Spring容器(配置扫描)
2.2.3、使用方法(从容器中拿对象)——启动类
3、总结对比
4、 常见问题:
🎯若依中Spring IOC、DI注解
1、定义实体类
2、定义Service接口
3、IOC:实现Service(关键:使用@Service)
4、DI:定义Controller,注入Service
5、Spring Boot启动类(环境初始化)
6、整个流程:
7、关键总结(Ruoyi中IOC/DI的使用模式)
🎯Spring MVC
1、🌟 Spring MVC 工作流程
2、注解
📌 1. @Controller —— “我是一个网页控制器”
📌 2. @RestController —— “我是一个 API 控制器”
📌 3. @RequestMapping —— “这个方法处理哪个路径?”
📌 4. @GetMapping, @PostMapping, @PutMapping, @DeleteMapping —— “我只处理哪种请求?”
📌 5. @RequestBody —— “请求体里的 JSON 怎么读?”
📌6. @ResponseBody —— “返回值怎么变成 JSON?”
📌7、总结对比
🎯若依中Spring MVC注解
📌示例:实现一个 “用户管理”接口,功能包括:
1️⃣ 第一步:定义统一返回结果类
2️⃣ 第二步:定义实体类(用户)
3️⃣ 第三步:定义 Service 接口与实现(供 Controller 调用)
4️⃣ 第四步:定义 Controller(核心!Spring MVC 注解集中地)
5️⃣ 第五步:Spring Boot 启动类(环境初始化)
总结:
🎯首先学习Spring IOC/DI
1、Spring IOC/DI整体框架

1.1 核心思想:
- 所有的对象都不用new,而是交给Spring容器创建和管理(IOC)
- 对象之间的依赖关系,由Spring自动“塞进去”(DI)
1.2 两个关键字:
- IOC(控制反转):对像的创建权从人-->交给Spring
- DI(依赖注入):对象的依赖关系有Spring自动填充
1.3 对象之间的依赖关系
- 对象之间的依赖关系=一个对象需要另一个对象才能工作(就像你吃饭需要筷子,你依赖筷子)
- 一个程序中的类需要别的类才能完成任务,这就是 “依赖”
在代码中:
@Servicepublic class OrderService{//这个OrderService需要UserService来查询永辉想你想@Autowired//自动注入UserService类private OrderService orderService;public void createOrder(){userService.getUserInfo();//直接使用UserService中的方法}}

2、真实案例(对比传统java和Spring(IOC和DI)):
用户下单时需要保存用户信息 ———— 搞懂IOC/DI
-
2.1 ❌ 场景一:传统java没有Spring——“自己动手,丰衣足食”
2.1.1、定义一个服务类
//定义一个服务类
//用户服务
public class UserService{public void saveUser(){System.out.println("保存用户到数据库");}
}
//订单服务(强依赖UserService)
public class OrderService{//问题1:硬编码new,耦合死死的private UserService userService=new UserService();public void createOrder(){System.out.println("创建订单.....");userService.saveUser();//调用}
}
2.1.2、使用方法
public class Main{public static void main(String[] args){//问题2:你自己控制一切,无法灵活替换实现OrderService orderService=new Override();orderService.createOrder();}
}
| 问题 | 后果 |
|---|---|
| new UserService()写死 | 换实现?改代码! |
| 对象的声明周期自己管 | 内存、单例、线程安全全靠手写 |
| 测试困难 | 想mock UserService? 几乎不可能 |
-
2.2 ✅ 场景二:使用 Spring(IOC + DI)—— “点菜式开发”
2.2.1、定义服务类(加注解)
//告诉Spring:请管理我
@Service
public class UserService{public void saveUser(){System.out.println("保存用户到数据库(由Spring管理");}
}
//告诉Spring:我也被管理,并且我需要UserService
@Service
public class OrderService{}
//不New 只声明“我需要他
@Autowired
private UserService userService;
public void createOrder(){System.out.println("创建订单(Spring注入依赖");userService.saveUser();
}
- @Service是告诉Spring:“这个类很重要,请帮我管理”
- @Autowired是说:“我需要某个对象,请Sping 自动给我塞进来”
- 但接下来的问题是:Spring怎么知道去哪里找到这些类?怎么启动Spring容器——这时候我们就需要写一个“启动开关(SpringConfig类)”
- SpringConfig作用:请从这个包开始扫描所有带@Component、@Service、@Controller的类,把他们都编程Bean放进容器里,然后开启服务!
2.2.2 、启动Spring容器(配置扫描)
//配置类:告诉Spring从那个包开始找@Compent类
//表示这是一个配置类,相当于以前的xml文件————>作用:告诉Spring这是你的说明书,请按这个来设置容器
@Configuration
//告诉Sring,请从这个包目录开始,自动扫描所有带@Component、@Service、@Controller、@Repository注解的类,把这些类都注册到Spring容器里面
//注册之后Spring就会自动发现这些类,并创建类对象放进容器里
@ComponentScan(basePackages="com.example.service")
public class SpringConfig{//空配置类即可
}
- 流程梳理:
启动程序时,Spring 会:- 读取
SpringConfig - 开始扫描
com.example.service包下的类 - 发现
UserService和OrderService,把它们创建成对象,放进容器 - 当
OrderService里有@Autowired private UserService userService;时 → Spring 自动把UserService对象注入进来 - 程序就可以正常运行了
- 补充:
@Configuration+@ComponentScan是告诉 Spring:“请从指定包里找出所有带注解的类,把它们变成对象放进来管理。
- 读取
- 为什么需要启动类:
@Configuration + @ComponentScan是“配置”阶段,告诉 Spring 去哪找类————但这还不够,我们需要一个“主程序”来启动真正的Spring容器,然后从容器里拿对象使用。
--因为java程序不会自己运行,必须有人启动他
--Spring容器不是魔法——它是个普通的java对象,需要人主动创建它,它才会开始工作(IOC /DI)
--本质:
①java是静态语言,代码不会自己跑起来
②Spring容器只是一个库(library),它不会在你编译或写代码时自动激活
③必须由你的程序主动创建容器实例,它才会开始扫描、注册、注入
2.2.3、使用方法(从容器中拿对象)——启动类
public class Main{public static void main(String[] args){//启动Spring容器(IOC容器)//AnnotationConfigApplicationContext:这是Spring容器提供的一个“启动器”,专门用来加载基于Java注解的配置//SpringConfig.class是请按照配置类来启动容器//即:请根据SpringConfig配置,启动Spring容器,开始扫描包,创建Bean、管理依赖ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);//从容器中获取对象(不是new)//context.getBean():是Spring提供的方法,用来从容器中“拿”对象//OrderService.class:你要的是哪一个类的对象//返回的是一个OrderService实例,这个实例已经被Spring创建好了,而且里面的userService也已经自动注入OrderService orderService=context.getBean(OrderService.class);//动用(userService已自动注入//最后调用方法执行逻辑//因为 userService 已经被 @Autowired 自动注入了,所以可以正常调用。orderService.createOrder();}
}
| 优势 | 说明 |
|---|---|
| 解耦 | OrderService不需要知道UserService怎么创建 |
| 灵活替换 | 只要实现相同的接口,换实现不用改一行代码 |
| 单例自动管理 | 默认每个Bean是单例,Spring保证线程安全 |
| 易于测试 | 单元测试时可轻松注入Mock对象 |
3、总结对比
| 维度 | 传统方式 | Spring(IOC+DI) |
|---|---|---|
| 谁创建对象? | 你自己new | Spring 容器创建 |
| 谁管理对象? | 你自己管生命周期 | Spring统一管理 |
| 依赖怎么来? | 手动new或者传参 | @Autowired自动注入 |
| 代码耦合度 | 高(紧耦合) | 低(松耦合) |
| 可维护性 | 差(改一改动全身) | 好(插拔式开发) |
- IOC是思想,DI是实现IOC的手段
4、 常见问题:
?Q:@Autowired是怎么知道注入那个对象的?
- 默认类型(TYPE)匹配
- 如果有多个同类型Bean,可以使用@Aualifier("beanName)指定名字
?Q:Spring容器什么时候创建这些对象
- 默认是singleton(单例),容器启动时就创建好
- 也可以设置为@Scope("prototype"),每次getBean都新建
?Q:一定药用注解吗,能不能用XML?
- 可以!早期用 XML 配置,现在主流是 注解 + Java Config
@Component+@Autowired是最现代、最简洁的方式
🎯若依中Spring IOC、DI注解
- 以实现“岗位管理”功能为例
包含: - SysPostController(控制器)
- ISysPostService(接口)+SyePostServiceImpl(实现)
- 使用@Autowired注入依赖
1、定义实体类

2、定义Service接口

3、IOC:实现Service(关键:使用@Service)

4、DI:定义Controller,注入Service

5、Spring Boot启动类(环境初始化)

6、整个流程:
┌──────────────────────┐ │ 启动 Spring │ │ (new AnnotationConfig│ │ ApplicationContext) │ └─────────┬────────────┘▼ ┌──────────────────────┐ │ 扫描指定包路径 │ │ (如:com.example) │ └─────────┬────────────┘▼ ┌─────────────────────────────┐ │ 发现 @Service 标注的类 │ │ → SysPostServiceImpl │ └─────────┬───────────────────┘▼ ┌──────────────────────┐ │ 创建 Bean 实例 │ │ 并注册到 Spring 容器 │ └─────────┬────────────┘▼ ┌─────────────────────────────┐ │ 发现 @RestController 标注的类│ │ → SysPostController │ └─────────┬───────────────────┘▼ ┌──────────────────────┐ │ 创建 SysPostController│ │ 的 Bean 实例 │ └─────────┬────────────┘▼ ┌───────────────────────────────────────┐ │ 执行依赖注入(DI) │ │ → 在 SysPostController 中 │ │ @Autowired │ │ private SysPostServiceImpl userService;│ │ → Spring 自动将容器中的 │ │ SysPostServiceImpl Bean 注入进来 │ └─────────┬─────────────────────────────┘▼ ┌──────────────────────┐ │ 等待 HTTP 请求到来 │ └─────────┬────────────┘▼ ┌──────────────────────┐ │ 调用 Controller 方法 │ │ → 方法内部使用 │ │ userService.doXXX() │ │ → 依赖正常工作 ✅ │ └──────────────────────┘补充:
- 整个过程由Spring容器触发
- @Service和@RestController都是@Component的派生注解,会被@CoponentScan自动发现
- @Autowired一来注入发生在Bean创建之后、请求处理之前
- 最终调用链:HTTP请求-->Controller-->Service,依赖已就绪
7、关键总结(Ruoyi中IOC/DI的使用模式)
| 步骤 | 注解 | 作用 |
|---|---|---|
| 1. 定义实现类 | @Service | 将自定义类型注册为 Spring Bean |
| 2. 声明依赖 | @Autowired | 自动从容器中查找匹配的 Bean 并注入 |
| 3. 启动容器 | @SpringBootApplication | 自动开启组件扫描 + IOC 容器初始化 |
🎯Spring MVC
1、🌟 Spring MVC 工作流程

- Spring MVC=前端发起请求-->Spring找到对于方法-->处理数据-->返回响应
2、注解
📌 1. @Controller —— “我是一个网页控制器”
- 作用:告诉 Spring:“这个类是用来处理前端请求的”
- 特点:
- 默认返回的是 HTML 页面
- 需要配合
@ResponseBody才能返回 JSON
📌示例

📌 2. @RestController —— “我是一个 API 控制器”
- 作用:等同于
@Controller + @ResponseBody - 特点:
- 自动把返回值转成 JSON
- 适合做 前后端分离项目(Vue/Angular/React)
📌示例

- 一句话总结:当用户访问
/user路径时,服务器会返回一个 JSON 格式的用户数据(而不是网页)。
📌 3. @RequestMapping —— “这个方法处理哪个路径?”
- 作用:绑定 URL 路径到方法
- 可以加在类上或方法上
📌 4. @GetMapping, @PostMapping, @PutMapping, @DeleteMapping —— “我只处理哪种请求?”
这些是 @RequestMapping 的简化写法,更清晰!
| 注解 | 对应 HTTP 方法 | 用途 |
|---|---|---|
@GetMapping | GET | 获取数据(查) |
@PostMapping | POST | 创建数据(增) |
@PutMapping | PUT | 更新数据(改) |
@DeleteMapping | DELETE | 删除数据(删) |
📌示例
@RestController
@RequestMapping("/api")
public class UserController {@GetMapping("/user/{id}") // GET /api/user/1public User getUser(@PathVariable Long id) {return new User("李四", 30);}@PostMapping("/user") // POST /api/userpublic String createUser(@RequestBody User user) {System.out.println("创建用户:" + user.getName());return "success";}@PutMapping("/user/{id}") // PUT /api/user/1public String updateUser(@PathVariable Long id, @RequestBody User user) {return "更新成功";}@DeleteMapping("/user/{id}") // DELETE /api/user/1public String deleteUser(@PathVariable Long id) {return "删除成功";}
}
📌 5. @RequestBody —— “请求体里的 JSON 怎么读?”
- 作用:把前端发来的 JSON 数据 自动转换成 Java 对象
- 必须配合
@PostMapping等使用

- 总结:这段代码定义了一个接收JSON数据的POST接口:当客户端(如前端、POSTMAN)发送一个包含用户信息的JSON到/user路径时,Spring会自动把JSON转换为Use对象,交给你处理
📌6. @ResponseBody —— “返回值怎么变成 JSON?”
- 作用:把方法返回的对象 自动转成 JSON 字符串
- 如果用了
@RestController,就不用写了(默认开启)
📌7、总结对比
| 注解 | 作用 | 什么时候用 |
|---|---|---|
@Controller | 处理请求,返回页面 | 做传统 Web 项目(JSP) |
@RestController | 处理请求,返回 JSON | 做前后端分离项目(Vue/React) |
@RequestMapping | 绑定 URL 路径 | 通用,可写在类或方法上 |
@GetMapping | 处理 GET 请求 | 查询数据 |
@PostMapping | 处理 POST 请求 | 提交表单、上传数据 |
@RequestBody | 读取请求体中的 JSON | 接收前端发来的数据 |
@ResponseBody | 把返回值转成 JSON | 和 @Controller 搭配使用 |
| 注解 | 作用 |
|---|---|
@Controller | 注册一个控制器,处理请求,返回页面 |
@RestController | 注册一个 API 控制器,返回 JSON(等于 @Controller + @ResponseBody) |
@RequestMapping | 绑定 URL 路径 |
@GetMapping | 处理 GET 请求 |
@PostMapping | 处理 POST 请求 |
@PutMapping | 处理 PUT 请求 |
@DeleteMapping | 处理 DELETE 请求 |
@RequestBody | 读取请求体中的 JSON |
@ResponseBody | 把返回值转成 JSON |
@Service | 注册一个服务类(Bean),供 Controller 使用 |
🎯若依中Spring MVC注解
Ruoyi 是一个 前后端分离 的权限管理系统,后端完全通过 RESTful API 返回 JSON。
因此,它大量使用:
@RestController(不是@Controller)@GetMapping/@PostMapping等 HTTP 方法映射@RequestBody接收前端传来的 JSON@PathVariable获取 URL 中的参数- 返回统一结果封装类
AjaxResult
💡 所有 Controller 都放在
com.ruoyi.web.controller包下
📌示例:实现一个 “用户管理”接口,功能包括:
- 查询部门详情(GET)
- 新增部门(POST,接收 JSON)
1️⃣ 第一步:定义统一返回结果类

源码:ruoyi-common.core.domain.AjaxResult
package com.ruoyi.common.core.domain;import java.util.HashMap;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.utils.StringUtils;/*** 操作消息提醒* * @author ruoyi*/
public class AjaxResult extends HashMap<String, Object>
{private static final long serialVersionUID = 1L;/** 状态码 */public static final String CODE_TAG = "code";/** 返回内容 */public static final String MSG_TAG = "msg";/** 数据对象 */public static final String DATA_TAG = "data";/*** 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。*/public AjaxResult(){}/*** 初始化一个新创建的 AjaxResult 对象* * @param code 状态码* @param msg 返回内容*/public AjaxResult(int code, String msg){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);}/*** 初始化一个新创建的 AjaxResult 对象* * @param code 状态码* @param msg 返回内容* @param data 数据对象*/public AjaxResult(int code, String msg, Object data){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);if (StringUtils.isNotNull(data)){super.put(DATA_TAG, data);}}/*** 返回成功消息* * @return 成功消息*/public static AjaxResult success(){return AjaxResult.success("操作成功");}/*** 返回成功数据* * @return 成功消息*/public static AjaxResult success(Object data){return AjaxResult.success("操作成功", data);}/*** 返回成功消息* * @param msg 返回内容* @return 成功消息*/public static AjaxResult success(String msg){return AjaxResult.success(msg, null);}/*** 返回成功消息* * @param msg 返回内容* @param data 数据对象* @return 成功消息*/public static AjaxResult success(String msg, Object data){return new AjaxResult(HttpStatus.SUCCESS, msg, data);}/*** 返回错误消息* * @return*/public static AjaxResult error(){return AjaxResult.error("操作失败");}/*** 返回错误消息* * @param msg 返回内容* @return 警告消息*/public static AjaxResult error(String msg){return AjaxResult.error(msg, null);}/*** 返回错误消息* * @param msg 返回内容* @param data 数据对象* @return 警告消息*/public static AjaxResult error(String msg, Object data){return new AjaxResult(HttpStatus.ERROR, msg, data);}/*** 返回错误消息* * @param code 状态码* @param msg 返回内容* @return 警告消息*/public static AjaxResult error(int code, String msg){return new AjaxResult(code, msg, null);}/*** 方便链式调用** @param key 键* @param value 值* @return 数据对象*/@Overridepublic AjaxResult put(String key, Object value){super.put(key, value);return this;}
}
2️⃣ 第二步:定义实体类(用户)

3️⃣ 第三步:定义 Service 接口与实现(供 Controller 调用)


4️⃣ 第四步:定义 Controller(核心!Spring MVC 注解集中地)

5️⃣ 第五步:Spring Boot 启动类(环境初始化)

总结:
- 所有 Controller 都是
@RestController - 路径统一用
@RequestMapping("/system/xxx") - 参数用
@PathVariable和@RequestBody - 返回值全是
AjaxResult
