一、为什么需要多类注解?
这些注解是为了匹配应用分层 ,让开发者通过注解直接识别类的用途(类似车牌标识车辆归属地),提升代码的可读性与分层清晰度。
| 注解 | 对应分层 | 作用描述 |
|---|---|---|
@Controller |
控制层 | 接收、处理请求并响应 |
@Service |
业务逻辑层 | 处理具体业务逻辑 |
@Repository |
数据访问层 | 负责数据库等数据操作 |
@Configuration |
配置层 | 处理项目配置信息 |
1.1类注解的底层关系
@Controller、@Service、@Repository等注解,本质是**@Component** 的衍生注解 (相当于@Component的 "子类"):
- 从源码可看到,这些注解都包含
@Component标识,因此它们的核心功能(将类注册为 Spring Bean)与@Component一致; - 区别仅在于语义化:不同注解对应不同分层场景,让代码的用途更直观(类似 "水杯""刷牙杯" 都是杯子,但用途更明确)。
1.2实际开发的选择原则
在对应分层中优先使用语义化注解(而非通用的@Component):
- 业务逻辑层用
@Service而非@Component; - 控制层用
@Controller而非@Component; - 这样能让代码的分层意图更清晰,便于团队协作与维护。
程序的应⽤分层,调⽤流程如下:

二、@Bean的适用场景
类注解(如@Service)无法满足以下场景时,需使用方法注解@Bean:
- 管理外部依赖的类:无法给外部包中的类添加类注解。
- 同一类需要多个对象 :比如项目需要多个数据源(同一
DataSource类需创建多个不同配置的对象)。
2.1@Bean的使用陷阱:必须配合类注解
单独使用@Bean无法将对象存入 Spring 容器,需满足:
@Bean标记的方法所在类,必须添加类注解 (如@Component、@Configuration),让 Spring 识别并扫描该类,进而执行@Bean方法创建对象。


第三方类(比如RestTemplate)的代码你无法修改,所以:
- 没法给它加
@Service等类注解,自然不会被 Spring 自动管理; - 你需要自己写一个方法 ,在方法里手动创建第三方类的对象(
new RestTemplate()); - 给这个方法加
@Bean注解,同时给方法所在类加@Configuration(或@Component)------ 这样 Spring 会扫描到这个类,执行@Bean方法,把第三方对象存为 Spring 的 Bean; - 之后就能用
@Autowired正常注入这个第三方对象(因为它已经被 Spring 管理了)。
核心总结:@Bean的作用,就是把 "你手动创建的对象(不管是自己写的还是第三方的)",纳入 Spring 的管理范围 ------ 本质是给第三方类 "补加" 了一个 "存对象" 的能力 ,补上之后,就能和自己的类一样用@Autowired了。

用第三方类,必须:
- 手写一个自己的配置类 (加
@Configuration/@Component); - 在配置类里写
@Bean方法,手动实例化第三方类的对象(相当于替 Spring 完成 "创建第三方对象并存入容器" 的工作); - 后续想用第三方类的功能时:
- 先通过
@Autowired注入这个 "被 Spring 管理的第三方对象"( - 再通过 "注入的对象。方法 ()" 调用功能.
- 先通过
2.2定义多个对象
针对同一个类(如User),通过@Bean可以定义多个不同配置的对象(比如多数据源场景),每个@Bean对应的方法会生成一个独立的 Bean 实例。
注意: 在一个类里定义多个同类对象」≠「必须用 @Bean」,@Bean只是Spring 容器管理多实例的一种方式,而非唯一方式;且「注入」是「使用对象」的动作,和「定义对象」是两回事。
2.2.1不同场景下定义多实例:
场景 1:第三方类(必须用 @Bean)
以RestTemplate(第三方类,无法加 @Component)为例,定义 2 个差异化配置的实例:

场景 2:自定义类(3 种方式)
以User(自定义类)为例,演示 3 种多实例定义方式:
方式 1:@Bean(配置类,支持差异化配置)

方式 2:@Component + @Scope(原型模式,配置逻辑一致)

方式 3:手动 new(非 Spring 管理)

三、使用多实例(注入时指定名称)
以场景 2 的方式 1 为例,注入并使用user1和user2:

关键区分:
| 维度 | 场景 1:第三方类(如 RestTemplate) | 场景 2:自定义类 - 方式 1(@Bean) | 场景 2:自定义类 - 方式 2(@Component+@Scope) | 场景 2:自定义类 - 方式 3(手动 new) |
|---|---|---|---|---|
| 是否需 Spring 管理 | 是(必须) | 是 | 是 | 否 |
| 定义方式 | 配置类 +@Bean | 配置类 +@Bean | 类上加 @Component+@Scope ("prototype") | 直接 new 对象 |
| 是否支持差异化配置 | 支持(每个 @Bean 独立配置) | 支持(每个 @Bean 独立配置) | 不支持(所有实例属性 / 逻辑一致) | 支持(手动设置不同属性) |
| Bean 名称 | @Bean ("名称") 指定(默认方法名) | @Bean ("名称") 指定 | 默认类名首字母小写(如 user) | 无 Bean 名称(非 Spring 管理) |
| 注入使用方式 | @Autowired+@Qualifier ("名称") | @Autowired+@Qualifier ("名称") | @Autowired(每次注入新实例,但配置一致) | 直接调用 new 出的对象(无注入) |
| 核心优势 | 能管理第三方类的多实例 | 灵活控制自定义类多实例配置 | 无需配置类,代码简洁 | 脱离 Spring,无容器依赖 |
| 核心局限 | 需写配置类 | 需写配置类 | 实例配置无法差异化 | 无法使用 Spring 的依赖注入 / 生命周期 |
| 典型使用场景 | 多数据源、多策略 RestTemplate | 自定义类多实例差异化配置 | 简单多实例(无需差异化) | 临时使用、非核心业务对象 |
结论:
关键结论
- 第三方类必选 @Bean:因为无法修改源码加 @Component,只能通过 @Bean 让 Spring 管理多实例;
- 自定义类优先选 @Bean:如果需要差异化配置(如 user1/zhangsan、user2/lisi),@Bean 是最优解;
- @Component+@Scope 仅适用于简单场景:仅需要 "多实例" 但不需要 "差异化配置" 时用;
- 手动 new 仅适用于非 Spring 管理场景:不需要依赖注入、生命周期管理时用。
四、扫描路径
Bean 通过@Component/@Bean等注解声明后,必须被 Spring 扫描到才能生效,否则 Spring 不会创建该 Bean,导致使用时找不到。
- 默认扫描范围 :
@SpringBootApplication内置了@ComponentScan,默认扫描启动类所在的包及其子包;若 Bean 不在这个范围内,会因未被扫描而不生效 - 手动补全扫描路径 :若 Bean 不在默认范围,可通过
@ComponentScan({"包路径1", "包路径2"})手动指定扫描包,但不推荐(易出错)。 - 推荐做法 :将启动类放在项目的根包下(如
com.example.demo),使所有业务包(component/configuration/controller等)都处于默认扫描范围内,无需额外配置。

五、DI详解
依赖注入(DI)是 IOC 容器在创建 Bean 时,自动为其提供所依赖的对象(资源)的过程;@Autowired是实现依赖注入的常用注解之一。
关于依赖注⼊,Spring也给我们提供了三种⽅式:
- 属性注⼊(FieldInjection)
- 构造⽅法注⼊(ConstructorInjection)
- Setter注⼊(SetterInjection)
5.1属性注⼊
属性注⼊是使⽤@Autowired 实现的,将Service类注⼊到Controller类中.
Service类的实现代码如下:

Controller类的实现代码如下:

获取Controller中的sayHi⽅法:

结果:

5.2构造⽅法注⼊ 构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所⽰:

注意事项:如果类只有⼀个构造⽅法,那么@Autowired注解可以省略;如果类中有多个构造⽅法, 那么需要添加上@Autowired来明确指定到底使⽤哪个构造⽅法。
5.3Setter注⼊
Setter注⼊和属性的Setter⽅法实现类似,只不过在设置set⽅法的时候需要加上@Autowired注 解,如下代码所⽰:

三种注入方式的优缺点对比
| 注入方式 | 优点 | 缺点 | 推荐版本 |
|---|---|---|---|
| 属性注入 | 代码简洁、使用方便 | 1. 仅适用于 IOC 容器,非 IOC 环境下会空指针;2. 无法注入 final 修饰的属性 | - |
| 构造方法注入 | 1. 可注入 final 属性;2. 依赖对象初始化时就确定,不会被修改;3. 通用性强(不依赖 IOC 容器) | 注入多个对象时,构造方法参数会较多,代码繁琐 | Spring 4.X+ |
| Setter 注入 | 可在实例创建后,重新配置 / 注入依赖对象 | 1. 无法注入 final 属性;2. 依赖对象可能被多次修改(Setter 可重复调用) | Spring 3.X(旧) |
- 构造方法注入是Spring 推荐的方式,更安全、通用性更强;
- 属性注入仅适用于简单场景,需注意其局限性;
- Setter 注入仅在需要 "动态修改依赖" 的场景下使用。
5.4@Autowired存在问题
当同⼀类型存在多个bean时,使⽤@Autowired会存在问题



5.4.1核心问题:
当同一个类(如User)被定义为多个 Bean 实例(如user1、user2)时,仅用@Autowired注入会触发 **"找到多个匹配 Bean" 的异常 **(因为 Spring 无法确定要注入哪个实例)。
5.4.2三种解决方法
1. @Primary:指定默认 Bean
在其中一个@Bean方法上添加@Primary,声明该 Bean 为 "默认实例",当@Autowired未指定具体 Bean 时,会自动注入这个默认实例。


2. @Qualifier:指定 Bean 名称
与@Autowired配合使用,通过value属性指定要注入的 Bean 名称,精准匹配实例。

3. @Resource:按名称注入
通过name属性指定 Bean 名称(是 JDK 提供的注解,非 Spring 专属),直接按名称匹配实例。

@Autowired与@Resource的区别
| 维度 | @Autowired |
@Resource |
|---|---|---|
| 所属框架 | Spring 提供 | JDK 提供 |
| 注入逻辑 | 默认按类型注入 | 默认按名称注入 |
| 支持的属性 | 仅配合@Qualifier指定名称 |
直接通过name属性指定名称 |
当同一类型存在多个 Bean 时,需通过@Primary(指定默认)、@Qualifier(配合@Autowired指定名称)或@Resource(按名称注入)来明确要注入的实例,避免冲突。