跳到主要内容Spring IoC 与 DI 核心知识点详解 | 极客日志Javajava
Spring IoC 与 DI 核心知识点详解
Spring IoC 容器通过 @Bean 和类注解管理对象生命周期。同一类可定义多个 Bean 并通过名称区分。Bean 命名遵循类名小驼峰或方法名规则,支持自定义。组件生效依赖扫描路径,@ComponentScan 可精确控制范围。依赖注入包含属性、构造器和 Setter 三种方式,构造器注入推荐。@Autowired 默认按类型注入,@Resource 按名称注入,多 Bean 场景需配合 @Primary 或 @Qualifier 解决冲突。
栈溢出6 浏览 Bean 配置与类注解
在 Spring 框架的设计中,方法注解 @Bean 要配合类注解才能将对象正常存储到 Spring 容器中。
@Component
public class BeanConfig {
@Bean
public User user() {
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
}
定义多个对象
对于同一个类,如何定义多个对象?比如多数据源的场景,类是同一个,但是配置不同,指向不同的数据源。
@Component
public class BeanConfig {
@Bean
public User user1() {
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("lisi");
user.setAge(19);
return user;
}
}
定义了多个对象的话,根据类型获取对象时可能会报错。报错信息显示:期望只有一个匹配,结果发现了两个。从报错信息中可以看出, 注解的 bean,bean 的名称就是它的方法名。
@Bean
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
User user1 = (User) context.getBean("user1");
User user2 = (User) context.getBean("user2");
System.out.println(user1);
System.out.println(user2);
可以看到,@Bean 可以针对同一个类,定义多个对象。
Bean 命名规则
1. 五大注解(@Component/@Service/@Controller/@Repository/@Configuration)
Spring 会根据类名自动生成 bean name,规则分两种:
- 情况 1:类名前两个字母都是大写 → bean name 直接等于类名本身
- 例:
class HTTPClient {} → bean name = HTTPClient
- 例:
class SQLParser {} → bean name = SQLParser
- 情况 2:其他情况 → bean name 是类名的小驼峰写法(首字母小写)
- 例:
class UserService {} → bean name = userService
- 例:
class OrderController {} → bean name = orderController
2. @Bean 注解
@Bean
public UserService userService() {
return new UserService();
}
→ bean name = userService
3. 重命名 Bean
可以通过设置 name 属性给 Bean 对象进行重命名操作:
@Bean(name = {"u1", "user1"})
public User user1() {
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
此时我们使用 u1 就可以获取到 User 对象了。
语法简写规则
扫描路径知识点
Q:使用 @Component/@Service 等四个注解声明的 bean,一定会生效吗?
A:不一定。原因:bean 想要生效,还需要被 Spring 扫描到,未被扫描的注解不会被注册为 bean。
如果运行的类在扫描五大注解的时候只扫描在同一个路径下的文件,如果把它放到 service 里面,其他路径的就不会扫描了。
@ComponentScan({"com.example.demo"})
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
User u1 = (User) context.getBean("u1");
System.out.println(u1);
}
}
这个异常表示:Spring 容器中不存在名为 u1 的 Bean。
- 可能原因:
@Bean(name = "u1") 的配置类没有被 Spring 扫描到。
@Bean 注解没有被正确添加到类注解(如 @Component/@Configuration)的类中。
- Bean 名称拼写错误。
注意:运行的这个类在扫描五大注解的时候只扫描在同一个路径下的文件。Spring Boot 的 @SpringBootApplication 已经包含了 @ComponentScan,默认扫描范围是'启动类所在的包 + 所有子包',所以大部分场景不用手动加。
但遇到以下情况,就需要手动配置 @ComponentScan:
- Bean 所在的包不在启动类的包 / 子包下;
- 想精确控制扫描范围;
- 想排除某些不想被扫描的类。
@ComponentScan 的核心用法
1. 基础用法:指定扫描的包路径
@ComponentScan({"com.example.service", "com.example.controller"})
@SpringBootApplication
public class SpringIocDemoApplication { ... }
- 语法:
@ComponentScan({"包路径 1", "包路径 2"}),大括号里可以写多个包;
- 注意:包路径要写完整的包名。
2. 进阶用法:包含 / 排除指定类
@ComponentScan(
basePackages = "com.example",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = UserService.class
)
)
@SpringBootApplication
public class SpringIocDemoApplication { ... }
这种用法适合临时排除某个类,或者只扫描特定类型的注解。
3. 和 @Configuration 的关联
@Configuration 标记的配置类,也需要被 @ComponentScan 扫描到,否则里面的 @Bean 方法不会被执行。
4. 关键注意点
- 不要重复扫描:手动加
@ComponentScan 时,不要和 @SpringBootApplication 的默认扫描范围重复。
- 包路径要写对:如果包路径写错,Spring 找不到注解类,会报
NoSuchBeanDefinitionException。
- Spring Boot 不推荐手动加:除非特殊场景,否则尽量把所有 Bean 放在启动类的包 / 子包下。
依赖注入详解
依赖注入(DI)是 IoC 容器在创建 Bean 时,为其提供运行所依赖资源(对象)的过程,也被称为'对象注入'或'属性装配'。
- 属性注入(Field Injection)
- 构造方法注入(Constructor Injection)
- Setter 注入(Setter Injection)
属性注入
属性注入通过 @Autowired 注解实现,将 Service 类注入到 Controller 类中。
@Service
public class UserService {
public void sayHi() {
System.out.println("Hi,UserService");
}
}
@Controller
public class UserController {
@Autowired
private UserService userService;
public void sayHi() {
System.out.println("hi,UserController...");
userService.sayHi();
}
}
构造方法注入
@Controller
public class UserController2 {
private UserService userService;
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
public void sayHi() {
System.out.println("hi,UserController2...");
userService.sayHi();
}
}
- 如果类只有一个构造方法,
@Autowired 注解可以省略;
- 如果类中有多个构造方法,需要添加
@Autowired 来明确指定使用哪个构造方法。
Setter 注入
Setter 注入通过类的 Setter 方法实现,需在 set 方法上添加 @Autowired 注解。
@Controller
public class UserController3 {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHi() {
System.out.println("hi,UserController3...");
userService.sayHi();
}
}
三种注入优缺点分析
属性注入
- 优点:简洁,使用方便;
- 缺点:只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常),不能注入一个 Final 修饰的属性。
构造函数注入 (Spring 4.X 推荐)
- 优点:可以注入 final 修饰的属性,注入的对象不会被修改,依赖对象在使用前一定会被完全初始化,通用性好;
- 缺点:注入多个对象时,代码会比较繁琐。
Setter 注入 (Spring 3.X 推荐)
- 优点:方便在类实例化之后,重新对该对象进行配置或者注入;
- 缺点:不能注入一个 Final 修饰的属性,注入对象可能会被改变。
@Autowired 存在问题及解决方案
当同一类型存在多个 bean 时,使用 @Autowired 会存在问题,报错原因是非唯一的 Bean 对象。
@Primary
@Qualifier
@Resource
1. 使用 @Primary 注解
当存在多个相同类型的 Bean 注入时,加上 @Primary 注解,来确定默认的实现。
@Component
public class BeanConfig {
@Primary
@Bean("u1")
public User user1() { ... }
@Bean
public User user2() { ... }
}
2. 使用 @Qualifier 注解
指定当前要注入的 bean 对象。在 @Qualifier 的 value 属性中,指定注入的 bean 的名称。
@Qualifier 注解不能单独使用,必须配合 @Autowired 使用。
@Controller
public class UserController {
@Qualifier("user2")
@Autowired
private User user;
}
3. 使用 @Resource 注解
是按照 bean 的名称进行注入。通过 name 属性指定要注入的 bean 的名称。
@Controller
public class UserController {
@Resource(name = "user2")
private User user;
}
@Autowired 与 @Resource 区别
- 来源不同:
@Autowired:由 Spring 框架 提供。
@Resource:由 JDK 提供(Java EE 标准),不依赖 Spring 框架。
- 注入规则不同:
@Autowired:默认**按类型(byType)**注入。若存在多个同类型 Bean,会再按属性名匹配,需配合 @Qualifier 使用。
@Resource:默认**按名称(byName)**注入。优先根据 name 属性或属性名匹配 Bean,若名称匹配不到,才会降级为按类型匹配。
- 功能与参数对比:
| 特性 | @Autowired | @Resource |
|---|
| 来源 | Spring 框架 | JDK (Java EE) |
| 默认注入方式 | 按类型 (byType) | 按名称 (byName) |
| 指定名称 | 需配合 @Qualifier | 直接用 name 属性 |
| 支持参数 | 仅 required | name, type, lookup 等 |
| 兼容性 | 仅 Spring 容器 | 通用,兼容所有支持 Java EE 的容器 |
@Resource:适合多实例场景,可直接指定名称获取目标 Bean。
@Autowired:适合单类型 Bean 场景,代码更简洁,Spring 生态中最常用。
- 在 Spring 4.3+ 后,构造方法注入成为官方推荐,
@Autowired 可省略,此时 @Resource 的优势主要体现在精确按名匹配上。
@Resource 属于 Java EE 规范,在高版本 JDK 中需要手动引入 jakarta.annotation-api 依赖才能使用。
- 若项目仅使用 Spring 生态,推荐优先用
@Autowired + @Qualifier;若需跨容器兼容,可考虑 @Resource。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online