Spring IOC/DI 与 MVC 从入门到实战

目录

[🎯首先学习Spring IOC/DI](#🎯首先学习Spring IOC/DI)

[1、Spring IOC/DI整体框架](#1、Spring IOC/DI整体框架)

[1.1 核心思想:](#1.1 核心思想:)

[1.2 两个关键字:](#1.2 两个关键字:)

[1.3 对象之间的依赖关系](#1.3 对象之间的依赖关系)

2、真实案例(对比传统java和Spring(IOC和DI)):

[2.1 ❌ 场景一:传统java没有Spring------"自己动手,丰衣足食"](#2.1 ❌ 场景一:传统java没有Spring——“自己动手,丰衣足食”)

2.1.1、定义一个服务类

2.1.2、使用方法

[2.2 ✅ 场景二:使用 Spring(IOC + DI)------ "点菜式开发"](#2.2 ✅ 场景二:使用 Spring(IOC + DI)—— “点菜式开发”)

2.2.1、定义服务类(加注解)

[2.2.2 、启动Spring容器(配置扫描)](#2.2.2 、启动Spring容器(配置扫描))

2.2.3、使用方法(从容器中拿对象)------启动类

3、总结对比

[4、 常见问题:](#4、 常见问题:)

[🎯若依中Spring IOC、DI注解](#🎯若依中Spring IOC、DI注解)

1、定义实体类

2、定义Service接口

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

4、DI:定义Controller,注入Service

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

6、整个流程:

7、关键总结(Ruoyi中IOC/DI的使用模式)

[🎯Spring MVC](#🎯Spring MVC)

[1、🌟 Spring MVC 工作流程](#1、🌟 Spring MVC 工作流程)

2、注解

[📌 1. @Controller ------ "我是一个网页控制器"](#📌 1. @Controller —— “我是一个网页控制器”)

[📌 2. @RestController ------ "我是一个 API 控制器"](#📌 2. @RestController —— “我是一个 API 控制器”)

[📌 3. @RequestMapping ------ "这个方法处理哪个路径?"](#📌 3. @RequestMapping —— “这个方法处理哪个路径?”)

[📌 4. @GetMapping, @PostMapping, @PutMapping, @DeleteMapping ------ "我只处理哪种请求?"](#📌 4. @GetMapping, @PostMapping, @PutMapping, @DeleteMapping —— “我只处理哪种请求?”)

[📌 5. @RequestBody ------ "请求体里的 JSON 怎么读?"](#📌 5. @RequestBody —— “请求体里的 JSON 怎么读?”)

[📌6. @ResponseBody ------ "返回值怎么变成 JSON?"](#📌6. @ResponseBody —— “返回值怎么变成 JSON?”)

📌7、总结对比

[🎯若依中Spring MVC注解](#🎯若依中Spring MVC注解)

[📌示例:实现一个 "用户管理"接口,功能包括:](#📌示例:实现一个 “用户管理”接口,功能包括:)

[1️⃣ 第一步:定义统一返回结果类](#1️⃣ 第一步:定义统一返回结果类)

[2️⃣ 第二步:定义实体类(用户)](#2️⃣ 第二步:定义实体类(用户))

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

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

[5️⃣ 第五步:Spring Boot 启动类(环境初始化)](#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 对象之间的依赖关系

  • 对象之间的依赖关系=一个对象需要另一个对象才能工作(就像你吃饭需要筷子,你依赖筷子)
  • 一个程序中的类需要别的类才能完成任务,这就是 "依赖"

在代码中:

java 复制代码
@Service
    public 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、定义一个服务类
java 复制代码
//定义一个服务类
//用户服务
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、使用方法
java 复制代码
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、定义服务类(加注解)
java 复制代码
//告诉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容器(配置扫描)
java 复制代码
//配置类:告诉Spring从那个包开始找@Compent类
//表示这是一个配置类,相当于以前的xml文件------------>作用:告诉Spring这是你的说明书,请按这个来设置容器
@Configuration
//告诉Sring,请从这个包目录开始,自动扫描所有带@Component、@Service、@Controller、@Repository注解的类,把这些类都注册到Spring容器里面
//注册之后Spring就会自动发现这些类,并创建类对象放进容器里
@ComponentScan(basePackages="com.example.service")
public class SpringConfig{
    //空配置类即可
}
  1. 流程梳理:

    启动程序时,Spring 会:

    • 读取 SpringConfig
    • 开始扫描 com.example.service 包下的类
    • 发现 UserService 和 OrderService,把它们创建成对象,放进容器
    • 当 OrderService 里有 @Autowired private UserService userService; 时 → Spring 自动把 UserService 对象注入进来
    • 程序就可以正常运行了
    • 补充: @Configuration + @ComponentScan 是告诉 Spring:"请从指定包里找出所有带注解的类,把它们变成对象放进来管理。
  2. 为什么需要启动类:

    @Configuration + @ComponentScan 是"配置"阶段,告诉 Spring 去哪找类------------但这还不够, 我们需要一个"主程序 "来启动真正的Spring容器,然后从容器里拿对象使用。
    --因为java程序不会自己运行,必须有人启动他
    --Spring容器不是魔法------它是个普通的java对象,需要人主动创建它,它才会开始工作(IOC /DI)
    --本质:
    ①java是静态语言,代码不会自己跑起来
    ②Spring容器只是一个库(library),它不会在你编译或写代码时自动激活
    ③必须由你的程序主动创建容器实例,它才会开始扫描、注册、注入

2.2.3、使用方法(从容器中拿对象)------启动类
java 复制代码
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对象
[Spring方式的优点]

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 删除数据(删)

📌示例

java 复制代码
@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/user/{id}")  // GET /api/user/1
    public User getUser(@PathVariable Long id) {
        return new User("李四", 30);
    }

    @PostMapping("/user")  // POST /api/user
    public String createUser(@RequestBody User user) {
        System.out.println("创建用户:" + user.getName());
        return "success";
    }

    @PutMapping("/user/{id}")  // PUT /api/user/1
    public String updateUser(@PathVariable Long id, @RequestBody User user) {
        return "更新成功";
    }

    @DeleteMapping("/user/{id}")  // DELETE /api/user/1
    public 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

java 复制代码
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 数据对象
     */
    @Override
    public 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
相关推荐
Scott9999HH4 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔4 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社5 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海5 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖6 小时前
JDK 26 新特性详解
java·开发语言
马优晨7 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区9 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大9 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai9 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说10 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json