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
相关推荐
早退的程序员1 小时前
记一次 Maven 3.8.3 无法下载 HTTP 仓库依赖的排查历程
java·http·maven
向阳而生,一路生花2 小时前
redis离线安装
java·数据库·redis
minji...2 小时前
C++ 面向对象三大特性之一---多态
开发语言·c++
Tigshop开源商城系统2 小时前
Tigshop 开源商城系统 php v5.1.9.1版本正式发布
java·大数据·开源·php·开源软件
散峰而望2 小时前
基本魔法语言函数(一)(C语言)
c语言·开发语言·编辑器·github
2401_841495642 小时前
【数据结构】基于BF算法的树种病毒检测
java·数据结构·c++·python·算法·字符串·模式匹配
little_xianzhong2 小时前
三个常听到的消息/中间件MQTT RabbitMQ Kafka
java·笔记·中间件·消息队列
论迹2 小时前
【Spring Cloud 微服务】-- 服务拆分原则
java·spring cloud·微服务
汤姆yu2 小时前
基于springboot的民间救援队救助系统
java·spring boot·后端·救援队