Spring IoC&DI
目录
1.Spring是什么?
Spring 是一个开源的 Java 开发框架,核心目标是简化企业级应用开发。它提供了模块化工具(如 Spring MVC、Spring Security、Spring Data 等),让开发者更专注于业务逻辑,而非重复的底层代码。也就是包含了众多方法的IoC容器,
容器是什么?
在 Spring 中,容器是一个 装对象的“盒子”,它负责创建、配置、组装你编写的 Java 对象(称为 Bean),并管理它们的生命周期。
Ioc(控制反转)是什么?
谁使用谁控制=>Spring来控制
是 Spring 的核心思想。传统代码中,对象自己控制依赖的创建(如 new Service());而 IoC 将控制权“反转”给容器——你只需声明依赖关系,容器自动注入所需对象。
例如:
// 传统方式:程序员自己创建依赖 UserService service = new UserService(); // IoC 方式:容器自动注入依赖 @Autowired private UserService service;简述Ioc和di
控制反转(IoC)的核心思想是将对象的创建和管理权从传统的“谁使用谁控制”模式转变为由框架(如Spring)统一管理。这种方式有效降低了组件间的耦合度,使系统更易于维护和扩展。
在IoC模式下,对象的生命周期完全由Spring容器管理,开发者无需手动实例化对象。依赖注入(DI)是IoC的一种实现方式,通过DI,Spring容器在运行时自动将依赖的对象注入到目标组件中,开发者只需声明依赖关系即可获得所需实例。
通过在类上面注释@Component:告诉Spring将该对象的控制权转给Spring
在类中注明依赖之后注释@Autowired:表明从Spring容器中拿出该对象并赋值给注明的依赖
2.详解Ioc
2.1Bean的存储
Spring为了更好地完善web服务提供许多注解能够完成对象的存储,代替@Component
类注解:@Controller,@Service,@Repository,@Component,@Configuration
方法注解:@Bean
类注解:
@Controller(控制器存储)
@Controller public class UserController { public String sayHello(){ return "Hello World!"; } }public static void main(String[] args) { //应用上下文 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args); //通过getBean方法观察是否能拿到UserController对象 //拿到说明通过@Controller,对象成功创建并且存储到Spring容器当中 UserController bean = context.getBean(UserController.class); System.out.println(bean.sayHello()); }

其他注解均可用同样的方法验证存储功能,如@Service:
@Service public class UserService { public String doService(){ return "doService"; } }@SpringBootApplication public class SpringIocDemoApplication { public static void main(String[] args) { //应用上下文 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args); UserService bean = context.getBean(UserService.class); System.out.println(bean); } }
因此后续类注解同上验证不做过多重复解释,既然起到效果相似,为什么还要有这么多?
原因是为了语义更加清晰,提高可读性,在对应的三层架构中使用对应的注解能更好的理解语义
// 不好的实践:因为其余注解都是@Component的衍生注解,因此都使用@Component
@Component
public class UserController { } // 这是控制器吗?服务吗?看不出来
@Component
public class UserService { } // 这是服务层吗?工具类吗?无法分辨
@Component
public class UserDao { } // 这是数据访问层吗?不清楚
// 好的实践:语义明确
@Controller
public class UserController { } // 明确是控制器,处理HTTP请求
@Service
public class UserService { } // 明确是服务层,处理业务逻辑
@Repository
public class UserDao { } // 明确是数据访问层
方法注解:开发人员自己创建对象交给Spring去管理,用于解决同一个类需要多个对象的问题/创建第三方对象
注意:需配合类注解使用
UserInfo类 @AllArgsConstructor//有参构造方法 @NoArgsConstructor//无参构造方法 @Data public class UserInfo { private String name; private int age; }
BeanConfig类 @Configuration public class BeanConfig { @Bean public UserInfo userInfo(){ return new UserInfo("zhangsan",12);//自主创建对象 } }
@SpringBootApplication public class SpringIocDemoApplication { public static void main(String[] args) { ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args); UserInfo bean1 = context.getBean(UserInfo.class); System.out.println(bean1); } }

可以看到能够通过@Bean使Spring得到对UserInfo对象的控制权
接下来解决多个对象创建的问题
@Configuration public class BeanConfig { //创建两个UserInfo对象,方法名区分开@Bean public UserInfo userInfo(){ return new UserInfo("zhangsan",12); } @Bean public UserInfo userInfo2(){ return new UserInfo("lisi",16); } }@SpringBootApplication public class SpringIocDemoApplication { public static void main(String[] args) { ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args); //只靠类型无法区分对象,因此传入对象名作为参数 UserInfo bean1 = context.getBean("userInfo",UserInfo.class); System.out.println(bean1); UserInfo bean2 = context.getBean("userInfo2",UserInfo.class); System.out.println(bean2); } }
2.2 Bean Name默认命名规则
(1)类注解
- 类名前两位为大写,如USerinfo=>bean name 为原类名
- 其他情况=>bean name 为类名的小驼峰形式
(2)方法注解
- bean name 为方法名
2.3 扫描路径
默认情况下,Spring Boot只扫描启动类所在包及其子包
但是可以通过@ComponentScan(basePackages="")来指定要扫描的路径
2.4@Autowired和@Resource区别
@Autowired是Spring提供的注解,根据类型进行注入
@Resource是JDK提供的注解,根据名称注入
2.5 @Autowired查找Bean顺序
- 按类型查找 Bean
- 根据依赖的类型(Class/Interface)在 Spring 容器中查找匹配的 Bean。
- 判断是否找到
- 如果未找到 →报错
- 如果找到 → 进入下一步判断。
- 是否配置了
@Qualifier参数?- 如果已配置
@Qualifier → 按@Qualifier指定的名称进一步筛选 Bean。- 如果找到唯一匹配 → 装配成功 ✅。
- 如果未找到或仍匹配多个 → 抛异常 ❌。
- 如果未配置
@Qualifier → 进入默认策略(按字段名匹配)。
- 如果已配置
- 字段名匹配
- 匹配成功→装配成功
- 未匹配→说明未指定名称,进入下一判断
- 是否只有一个Bean
- 如果只有一个bean → 直接装配。
3.详解DI
依赖注入是一个过程,是ioc容器创建Bean的过程中提供所依赖的资源,这个资源就是对象
Spring提供了三种方式:
(1)属性注入@Autowired
@Component
public class UserService {
// 方式1:直接注入到属性
@Autowired
private UserRepository userRepository;
}
(2)构造方法注入
@Service
public class UserService {
// 使用final,确保依赖不可变
private final UserRepository userRepository;
// 构造方法注入
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
注意:
- 只有一个构造方法时默认执行这一个
- 多个构造方法默认执行无参构造,若没有无参构造则报错
- 可通过@Autowired注解指定执行的构造方法
(3)Setter注入
@Service
public class UserService {
private UserService userService;
// Setter注入
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}
Spring三种依赖注入方式优缺点对比
对比维度 | 构造方法注入 | Setter注入 | 属性注入 |
|---|---|---|---|
代码简洁性 | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
可读性 | ✅ 优秀(依赖一目了然) | ✅ 良好 | 差(依赖关系隐藏) |
不可变性 | ✅ 支持final(线程安全) | 不支持final | 不支持final |
单元测试 | ✅ 最容易(直接传参) | ✅ 容易 | 困难(需要反射) |
依赖完整性 | ✅ 保证(对象创建即完整) | 不保证(可能部分注入) | 不保证 |
循环依赖检测 | ✅ 启动时报错 | ✅ Spring可处理 | ✅ Spring可处理 |
可选依赖支持 | ⭐ 有限(需Optional包装) | ✅ 优秀(required=false) | ✅ 支持 |
框架耦合度 | ⭐ 低 | ⭐ 低 | ✅ 高(强依赖Spring) |
代码重构 | ⭐⭐⭐ | ⭐⭐⭐⭐ | ✅ 最容易 |