
一.IOC&DI 介绍
1. 传统程序开发 的问题 : 高耦合
以 "造一辆车" 为例,传统开发中对象的创建和依赖关系由自身控制:汽⻋依赖⻋⾝,⻋⾝依赖底盘,底盘依赖轮 ;
所有的对象都通过 new 手动创建 ; 当底层组件(如轮胎尺寸) 发生变化时 , 整个调用链上的所有代码都需要修改 , 程序耦合度高 , 可维护性差

java
public class Main {
public static void main(String[] args) {
Car car = new Car(21);
car.run();
}
}
public class Bottom {
private Tire tire;
public Bottom(Integer size) {
this.tire = new Tire(size);
System.out.println("bottom init...");
}
}
public class Car {
private Framework framework;
public Car(Integer size) {
this.framework = new Framework(size);
System.out.println("car init...");
}
public void run() {
System.out.println("car run...");
}
}
public class Framework {
private Bottom bottom;
public Framework(Integer size) {
this.bottom = new Bottom(size);
System.out.println("framework init...");
}
}
public class Tire {
int size;
public Tire(Integer size) {
this.size = size;
System.out.println("tire init, size:"+ size);
}
}
注意 : 上述代码分为 5 个类

随着车辆的个性化需求增多 , 如果我们修改代码 , 会发现修改成本很高 , 例如 :

从上述代码可以看出 , 问题出现在 : 当最底层代码改动后 , 整个调用链上的所有代码都需要修改
在上⾯的程序中, 我们是根据轮⼦的尺⼨设计的底盘,轮⼦的尺⼨⼀改,底盘的设计就得修改.
同样因为我们是根据底盘设计的车身,那么车身也得改,同理汽⻋设计也得改,整个设计⼏乎都得改
2. IoC (Inversion of Control,控制反转)
定义:Spring 是一个 "控制反转" 的容器,本质是对象的创建权 由程序自身反转给 Spring 容器
- 传统模式 :程序需要主动通过
new关键字创建对象 - IoC 模式:对象的创建和管理交给 IoC 容器,程序只需从容器中获取对象即可
- IoC 的核心价值:实现程序解耦,将对象之间的依赖关系从代码中剥离,由容器统一管理,底层组件变化时,上层代码无需修改
举一个例子直观理解 IoC 思想 : 自动驾驶
- 传统驾驶 : 车辆的控制权由驾驶员掌握
- 自动驾驶 : 控制权反转 , 交给驾驶自动化系统处理
3. 解决方式 : DI (Dependency Injection,依赖注入)
它是实现 IoC 的主要方式 , Spring 容器在创建对象时,容器会动态地为程序提供运行时所以来的资源(对象)
- 关系 : Ioc 是思想/目标 , DI 是现实手段 , 二者在不同的角度描述这同一间事情--解耦对象依赖
举个例子 : 理解 IOC 和 DI 之间的关系
- "想吃个好的"(Ioc 思想) , 选择"吃火锅"或者"吃烤肉"(DI 具体实现) , 思想指导实现 , 实现落地思想
容器在运行期间 , 动态的为应用程序提供运行时依赖的资源
我们尝试换一种思路 , 先设计车身 , 根据车身来设计底盘 , 根据底盘来设计轮子 ; 得到依赖关系 : 轮子依赖底盘 , 底盘依赖⻋车身 , 车身依赖汽车

只需要将原本由自己创建的下级类 , 改为**传递(注入)**的方式
通过构造函数的方式 , 把依赖对象注入到需要使用的对象中
java
public class main {
//注意别调用成v1版本的构造方法
public static void main(String[] args) {
Tire tire = new Tire(2,"red");
Bottom bottom = new Bottom(tire);
Framework framework = new Framework(bottom);
Car car = new Car(framework);
car.run();
}
}
public class Tire {
int size;
String color;
public Tire(Integer size , String color){
this.color = color;
this.size = size;
System.out.println("tire init:"+color + size);
}
}
public class Bottom {
private Tire tire;
public Bottom(Tire Tire){
this.tire = tire;
System.out.println("bottom init....");
}
}
public class Framework {
private Bottom bottom;
public Framework(Bottom bottom){
this.bottom = bottom;
System.out.println("Framework init....");
}
}
public class Car {
private Framework framework;
public Car(Framework framework){
this.framework = framework;
System.out.println("car init...");
}
public void run(){
System.out.println("car run...");
}
}
通过上述调整 : 无论底层如何变化 , 整个调用链不做任何变化 , 这样就 完成了代码的解耦

4.IoC 容器的核心能力
Spring 作为 IoC 容器 , 核心只做两件事 :
- 存 : 将对象(Bean)交给 Spring 容器管理
- 取 : 程序需要时 , 从 Spring 容器中获取依赖的 Bean 对象
三 . IoC 实战 : Bean 的存储(将对象交给 Spring 管理)
将对象交给 Spring 容器管理 , 即 Bean 的注册 , Spring 提供类注解和方法注解两种方式 , 其中类注解是日常开发的主流 , 方法注解用于特殊场景
1. 类注解
|----------------|----------------|----------------|
| 注释 | 对应分层 | 核心作用 |
| @Controller | 控制层(Web) | 接收请求,处理请求,响应结果 |
| @Service | 业务逻辑层(Service) | 处理具体的业务逻辑 |
| @Repository | 数据访问层(Dao) | 负责数据库/数据源操作 |
| @Configuration | 配置层 | 处理项目的配置信息 |
| @Component | 通用组件层 | 非分层的通用组件注册 |
1.1 @Component(通用注解)
核心作用 : 最基础的注解 , 告诉 Spring "这个类需要被 IoC 容器实例化并管理 " , 是其他 4 个注解的"父注解" (其它注解本质上就是对@Controller 的特殊化)
使用场景 : 当你的类不属于 Controller/Service/Repository/Configuration 任何一层 , 又需要被 Spring 管理时使用(如工具,组件)
java
package com.boop.springioc.TestNode.Component;
import org.springframework.stereotype.Component;
@Component
public class UserComponent {
public void print(){
System.out.println("do Component");
}
}
java
package com.boop.springioc;
import com.boop.springioc.TestNode.Component.UserComponent;
import com.boop.springioc.TestNode.Controller.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
//获取Spring上下文
ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args);
//测试Conponent
UserComponent userComponent = context.getBean(UserComponent.class);
userComponent.print();
}
}

注意 :
① 如果手动删除@Coponent

报错信息 : 找不到类型是'com.boop.springioc.TestNode. Component.UserComponent'的 bean
②ApplicationContext 获取 bean 对象的功能 , 是父类 BeanFactory 提供的

③ 默认 bean 名称 :

根据 Bean 的命名规则 , 来手动获取 Bean
java
package com.boop.springioc;
import com.boop.springioc.TestNode.Component.UserComponent;
import com.boop.springioc.TestNode.Controller.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
//获取Spring上下文
ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args);
//测试Conponent
UserComponent userComponent1 = context.getBean(UserComponent.class);
userComponent1.print();
UserComponent userComponent2 = (UserComponent)context.getBean("userComponent");
userComponent2.print();
System.out.println(userComponent1);
System.out.println(userComponent2);
}
}


1.2@Controller(表现层注解)
核心作用 : 标记类为 SpringMVC 的控制器(处理 HTTP 请求) , 并且是 Web 层的组件
额外特性 :
- 配合@RequestMapping 等注解处理特殊请求
- Spring MVC 的异常处理器 , 参数绑定等功能会优先识别该类注解标记的类
- 本质:
@Controller=@Component + Web层语义
java
package com.boop.springioc.TestNode.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@RequestMapping("/usctrl")
@ResponseBody
@Controller
//@Service
public class UserController {
@RequestMapping("/sayHi")
public void sayHi(){
System.out.println("hi,UserController...");
}
}
java
package com.boop.springioc;
import com.boop.springioc.TestNode.Controller.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
//获取Spring上下文
ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args);
//测试@Controller
//使用Spring上下文获取对象
UserController userController = context.getBean(UserController.class);
//使用对象
userController.sayHi();
}
}

注意事项 :
① Spring 上下文对象 :
Spring 上下文对象(ApplicationContext) :
本质是 : Spring 框架的核心容器+运行上下文环境
是 Spring 整个应用的"总控室"
它继承了多个核心接口 , 具备 : IoC 容器核心 , 环境与配置管理 , 资源加载器 , 事件发布/监听 , 国际化支持 , 应用层上下文整合
② 为什么还要加 @ResponseBody
- 如果只加 @Controller 时 , Spring 会把返回值当作视图名取寻找模板(如 xxx.html) ; 示例中的方法 sayHI() 返回值是 void , 如果要直接输出字符串/JSON 给前端 , 必须告诉 Spring 这是响应体 , 而不是页面 ; 此时就需要用到 @ResponseBody , 加在类上 , 并且表示类的所有接口都直接返回数据 , 不找视图
- 我还想让方法 sayHI() 成为一个可以访问的 HTTP 接口 , 就必须加@ResponseBody,@RequestMapping 等注解 ; 如果方法 sayHI() 只是在内部调用 , 不对外提供接口 , 那就不需要加@ResponseBody
1.3@Service(业务层注解)
核心作用 : 标记类为业务逻辑层组件 , 语义上明确这是"处理核心业务逻辑" 的类
额外特性 :
- 语义化强 , 便于团队协作和代码维护
- 本质 :
@Service=@Component + 业务层语义
java
package com.boop.springioc.TestNode.Service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void print(){
System.out.println("do Service");
}
}
java
import...//此处省略
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
//获取Spring上下文
ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args);
//测试@Service
UserService userService = context.getBean(UserService.class);
userService.print();
}
}

省去@Service 同样报错

1.4@Repository(数据访问层注解)
核心作用 : 标记类为数据访问层(Dao)组件 , 负责与数据库/数据源交互
额外特性 :
- 自动转换 JDBC 相关的异常(将原生 SQL 异常转化为 SPring 统一的 DataAccessException)
- 语义上明确这是"数据访问层" , 是持久化操作的核心
- 本质 :
@Repository=@Component + 数据访问层语义 + 异常转换
java
package com.boop.springioc.TestNode.Respository;
import org.springframework.stereotype.Repository;
@Repository
public class UserRepository {
public void print(){
System.out.println("do Respository");
}
}
java
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
//获取Spring上下文
ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args);
//测试Repository
UserRepository userRepository = context.getBean(UserRepository.class);
userRepository.print();
}
}

删掉@Repository 同样报错
1.5@Configuration(配置类注解)
核心作用 : 标记类为 Spring 的配置类 , 替代传统的 XML 配置文件 , 用于定义 Bean,配置依赖等
额外特性 :
- 配合@Bean 注释可以手动注册 Bean(方法即注解)
- 配置类本身也是一个 Bean , 但优先级高于普通 @Component
- 支持@ComponentScan(扫描指定包下的注解) , @Import(导入其他配置类)等
- 本质 :
@Configuration=@Component + 配置类语义 + 增强的Bean定义能力
java
package com.boop.springioc.TestNode.Config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfig {
public void print(){
System.out.println("do config");
}
}
java
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
//获取Spring上下文
ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args);
//测试@Configuration
UserConfig userConfig = context.getBean(UserConfig.class);
userConfig.print();
}
}

同样的删除@Configuration, 也会报错
未完!!!
由于内容较长,下一篇将聚焦剩下的方法注解进行讲解:包括 : @Bean , @Autowired , 以及详细讲解@Autowired失效的解决方案。
关注我,下篇持续更新,避免错过完整实战流程~ 也欢迎在评论区留言你遇到的问题,下篇会针对性解答!