了解 Spring
1、IOC(控制反转)和 AOP(面向切面)
-
IOC (Inversion of Control) 控制反转:
- 原理:将对象的创建权和管理权从“开发者手中”转移到“Spring容器手中”。
- 好处:解耦。对象不在代码中硬编码
new出来,而是由容器注入,方便替换和维护。 - 底层实现:反射 + XML/注解解析 + Map容器(单例池)。
-
AOP (Aspect Oriented Programming) 面向切面:
- 原理:将通用的逻辑(日志、事务、权限)从业务代码中剥离出来,横向切入到业务流程中。
- 好处:避免代码重复,让业务类专注于业务逻辑。
- 底层实现:动态代理(JDK动态代理基于接口,CGLIB基于继承)。
2、DI(依赖注入)
- 定义:是 IOC 的具体实现方式。Spring 在创建对象时,自动将对象依赖的属性赋值。
- 实现方式:
- 字段注入:
@Autowired直接加在成员变量上(最常用,但不推荐用于强制依赖)。 - 构造器注入:加在构造函数上(Spring官方推荐,保证对象创建时属性不为空,不可变对象)。
- Setter注入:加在Setter方法上(可选依赖)。
- 字段注入:
- 底层逻辑:反射 + BeanPostProcessor(后置处理器)。
3、常见注解解释
| 注解 | 分类 | 作用与解释 | 区别/注意点 |
|---|---|---|---|
| @Component | 分层 | 通用的组件注解,标记为Spring Bean。 | 当不属于Controller/Service/Dao时使用。 |
| @Controller | 分层 | 标记表现层组件。 | Spring MVC会识别它为处理器,能接收HTTP请求。 |
| @Service | 分层 | 标记业务逻辑层组件。 | 语义清晰,目前无特殊技术功能。 |
| @Repository | 分层 | 标记数据访问层组件。 | 能自动捕获数据库异常并转换为Spring统一异常。 |
| @Mapper | 分层 | MyBatis提供的注解,标记数据接口。 | 生成代理实现类,让你只写接口不写实现类就能操作数据库。 |
| @Autowired | 注入 | Spring原生注解,自动注入依赖。 | 默认按类型找。如果有多个同类型,需配合@Qualifier按名称找。 |
| @Resource | 注入 | JDK标准注解(JSR-250),自动注入依赖。 | 默认按名称找。如果名称找不到,再按类型找。通用性更强。 |
| @PostConstruct | 生命周期 | 标记初始化方法。 | 在构造函数执行后、依赖注入完成后执行。用于初始化资源(如加载缓存)。 |
| @Transactional | AOP | 声明式事务管理。 | AOP的典型应用。开启事务,方法异常时回滚。注意:类内部调用会导致事务失效(绕过了代理)。 |
4、单例和多例
| 特性 | 单例 | 多例 |
|---|---|---|
| 配置 | @Scope("singleton") 或默认不写 | @Scope("prototype") 必须显式声明 |
| 生命周期 | 容器启动创建,容器销毁消亡。 | 每次获取时创建,Spring只管创建不管销毁。 |
| 内存情况 | 全局唯一,所有引用指向同一内存地址。 | 每次获取产生新对象,互不干扰。 |
| 线程安全 | 需注意!不要定义可变的成员变量,否则会有线程安全问题。 | 相对安全,每个线程有自己的实例。 |
| 性能 | 高(对象复用)。 | 低(频繁创建销毁)。 |
5、循环依赖
-
场景:A 依赖 B,B 又依赖 A。
-
Spring 解决方案(三级缓存):
- Spring 通过提前暴露“半成品”对象(刚创建但还没注入属性的 A)放入缓存,让 B 先拿到 A 的引用,从而打破了循环。
- 适用范围:仅适用于单例且非构造器注入的情况。
-
延迟注入注解:
@Lazy- 作用:解决循环依赖(特别是构造器注入导致的循环依赖)。
- 原理:Spring 不会立即创建真实的依赖对象,而是创建一个代理对象注入进去。
- 流程:
- 创建 A 时,发现依赖 B。
- 因为 B 标记了
@Lazy,Spring 注入一个 B 的代理对象给 A。 - A 创建完成。
- 创建 B 时,B 依赖 A,此时 A 已经存在,直接注入。
- 真正调用 B 的方法时,代理对象才会去容器中查找真正的 B 实例执行逻辑。