文章目录
- [Ⅰ. 什么是 `IOC` 和 `DI`❓❓❓](#Ⅰ. 什么是
IOC和DI❓❓❓) - [Ⅱ. 五大注解](#Ⅱ. 五大注解)
- [Ⅲ. 注解 `@Bean`](#Ⅲ. 注解
@Bean) - [Ⅳ. 扫描路径 `@ComponentScan`](#Ⅳ. 扫描路径
@ComponentScan) - [Ⅴ. 依赖注入](#Ⅴ. 依赖注入)
-
- [一、三种注入方式 `@Autowired`](#一、三种注入方式
@Autowired) -
- [① 属性注入](#① 属性注入)
- [② 构造方法注入](#② 构造方法注入)
- [③ `Setter`方法注入](#③
Setter方法注入) - 三种注入方式的区别
- [二、`@Autowired` 存在的问题](#二、
@Autowired存在的问题) -
- [① `@Primary`](#①
@Primary) - [② `@Qualifier`](#②
@Qualifier) - [③ `@Resource`⭐⭐⭐](#③
@Resource⭐⭐⭐)
- [① `@Primary`](#①
- [一、三种注入方式 `@Autowired`](#一、三种注入方式

Ⅰ. 什么是 IOC 和 DI❓❓❓
IOC 全称是 Inversion of Control,中文叫 "控制反转"。
简单的说,就是把对象的创建和依赖关系的维护交给框架管理,而不是自己在代码中 new 出来。
IOC 是一种设计思想 ,而注解 DI 是实现 IOC 的方式之一。
IOC 的好处如下所示:
| 优点 | 说明 |
|---|---|
| 解耦 | 组件之间不直接 new,依赖由框架注入 |
| 易测试 | 方便用 mock 对象进行单元测试 |
| 灵活替换 | 想换实现类,只改配置或注解,不改业务代码 |
| 更清晰的架构 | 各层职责分明,依赖关系可视化 |
此外,Spring 容器管理的对象,称为 Bean 对象。
@Component 和 @Autowired 是 Spring 框架中非常核心的两个注解,是实现 IOC(控制反转) 和 DI(依赖注入) 的关键工具。
@Component:一个类级注解,用于将普通的Java类声明为Spring管理的Bean。@Autowired:一个非类级注解,用于自动注入依赖对象,而无需手动new。
如何从
Spring容器中获取对象❓❓❓可以从
ApplicationContext中的getBean()方法获取,如下所示:
javapublic static void main(String[] args) { // 在启动类中拿到ApplicationContext对象 ApplicationContext context = SpringApplication.run(Application.class, args); // 第一种传参方式 UserComponent bean1 = context.getBean(UserComponent.class); bean1.func(); // 第二种传参方式(需要强转) UserConfig bean2 = (UserConfig)context.getBean("userConfig"); bean2.func(); }而
getBean()方法,实际上是ApplicationContext是实现了BeanFactory接口 得到的,并且在其基础上添加了对国际化支持、资源访问支持、以及事件传播等方面的支持! 此外,getBean()对于Bean的名称约定如下所示:
- 以小写字母开头,然后使用小驼峰的格式 。如类名为:
UserController,则Bean名为:userController。- 特殊情况:当第一个和第二个字母为大写时,则保留原始的大小写 。如类名为:
UController,则Bean名为UController。
Ⅱ. 五大注解
| 注解 | 作用 | 常见使用位置 |
|---|---|---|
| @Component | 通用组件,标识该类为 Bean |
工具类、业务组件 |
| @Controller | 控制层,接收前端请求 | MVC 控制器类 |
| @Service | 业务逻辑层 | Service 类 |
| @Repository | 数据持久层 | DAO 类,MyBatis Mapper |
| @Configuration | 配置类 | 用于代替 XML 配置 |
常见的目录结构规范如下所示:
javascript
src
└── main
└── java
└── com.example.project
├── controller # 控制器层(@Controller / @RestController)
├── service # 业务逻辑层(@Service)
├── dao/mapper # 数据访问层(@Repository / @Mapper)
├── model/entity # 实体类(POJO/DTO/DO/VO)
└── config # 配置类(@Configuration)
注意:五大注解均可以起别名!
Ⅲ. 注解 @Bean
@Bean 告诉 spring,这个方法返回的对象要放进 IoC 容器(ApplicationContext)里,成为可被任何其他 Bean 注入的组件。
💥注意事项:
-
@Bean要配合五大注解使用,不能单独使用 -
在
Spring中,默认情况下Bean的作用域是singleton(单例)的,即整个Spring容器中只存在一个该Bean实例。 -
对于同一个类,要定义多个
Bean对象的话,需要对不同的方法进行注解,然后使用ApplicationContext对象的getBean()方法中传入Bean名称的方式进行获取,对应的Bean名称是对应方法的名称 ,当然也可以进行重命名,默认重命名的属性是name,如下所示java@Component public class UserComponent { @Bean("{u3}") public User u1() { return new User(); } @Bean public User u2() { return new User(); } } public static void main(String[] args) { ApplicationContext context = SpringApplication.run(Application.class, args); // 通过 Bean 名获取,不然 Spring 会报错,识别不出要哪个对象 // 并且可以对 Bean 对象进行重命名! User u1 = (User)context.getBean("u3"); User u2 = (User)context.getBean("u2"); System.out.println(u1); System.out.println(u2); } // 运行结果: com.liren.ioc.model.User@abbe000 com.liren.ioc.model.User@3f81621c
Ⅳ. 扫描路径 @ComponentScan
Spring 默认扫描的范围是 SpringBoot 启动类所在包及其子包,如下图所示,一般也推荐直接把启动类放到项目目录中!

但是如果需要放在特定包内,还需要访问其它非子包的包内,则需要使用 @ComponentScan 来添加要扫描的包,如下所示,当然也可以用 {} 配置多个包路径!
java
@ComponentScan("com.liren.ioc.service") // 指定扫描com.liren.ioc.service包中的内容
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Ⅴ. 依赖注入
一、三种注入方式 @Autowired
① 属性注入
这种方式虽然不是官方最推荐的,但却是日常开发最常用的。
java
@Controller
public class UserController {
@Autowired
private UserService userService;
public void func() {
System.out.println("UserController");
userService.func();
}
}
② 构造方法注入
💥注意: 如果类只有一个构造方法,那么 @Autowired 注解可以省略;如果类中有多个构造方法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法。
java
@Controller
public class UserController {
private UserService userService;
// 默认构造方法
public UserController() {}
// 如果有默认构造方法,那么不显式写上@Autowired的话,会去调用默认构造方法
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public void func() {
System.out.println("UserController");
userService.func();
}
}
③ Setter方法注入
java
@Controller
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void func() {
System.out.println("UserController");
userService.func();
}
}
三种注入方式的区别
- 属性注入
- 优点: 简洁,使用方便
- 缺点:
- 只能用于
IOC容器,如果是非IOC容器不可用,并且只有在使用的时候才会出现空指针异常 - 不能注入一个
Final修饰的属性
- 只能用于
- 构造函数注入 (Spring 4.X 推荐)
- 优点:
- 可以注入
final修饰的属性 - 注入的对象不会被修改
- 依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法。
- 通用性好,构造方法是
JDK支持的,所以更换任何框架,它都是适用的
- 可以注入
- 缺点:
- 注入多个对象时,代码会比较繁琐
- 优点:
- Setter 注入 (Spring 3.X 推荐)
- 优点: 方便在类实例之后,重新对该对象进行配置或者注入
- 缺点:
- 不能注入一个
final修饰的属性 - 注入对象可能会被改变,因为
setter方法可能会被多次调用,就有被修改的风险
- 不能注入一个
二、@Autowired 存在的问题
当同一个类存在多个 Bean 时,使用 @Autowired 会存在问题,如下所示:

如何解决上述问题呢❓❓❓Spring 提供了以下几种解决方案:
@Primary@Qualifier@Resource
① @Primary
当存在多个相同类型的 Bean 注入时,加上 @Primary 注解,来确定默认的实现!
这个注解加在要被指定注入的 Bean 对象上,如下所示:
java
@Component
public class UserComponent {
@Primary
@Bean
public User u3() {
return new User();
}
@Bean
public User u4() {
return new User();
}
}
② @Qualifier
注意该注解不能单独使用,需要配合 @Autowired 才行!
java
@Controller
public class UserController2 {
@Qualifier("u3") // 指定对应Bean的名称
@Autowired
private User user;
public void func() {
System.out.println("UserController2");
}
}
③ @Resource⭐⭐⭐
该注解可以单独使用,不过需要显式用属性 name 来指定对应的 Bean 对象!
java
@Controller
public class UserController2 {
@Resource(name = "u3") // 需要显式写一下name来指定
private User user;
public void func() {
System.out.println("UserController2");
}
}
需要注意的是,@Resource 是 JDK 自带的,支持更多的参数设置 ,而 @Autowired 是 Spring 框架提供的,没有前者功能那么多!
@Autowired 的装配顺序如下图所示:

