IOC
IOC就是控制反转。也就是把创建对象管理对象等事情交给spring容器来做。
举例:传统的开发就是A依赖于B,那就是A手动new一个对象出来;springIOC就是spring来帮我们实例化对象,我们需要什么对象直接在spring容器里取出来就是了。
用一个技术就要看解决了什么问题,优点。而IOC的优点是解决了对象之间的耦合性,不会牵一发而动全身。
举例:service依赖dao层
无IOC:每次使用dao层实现类的增删改查就需要new这个实现类,如果有新的实现代码还要改业务代码。
有IOC:spring的配置时代就改配置的对应实现类就行,springboot的注解时代就是Qualifier注解注明就行。
AOP
AOP就是面向切面编程。就是把横切的地方从核心业务中分离出来,也是一种解耦。
通知就是在哪里触发,切面就是对关注点封装的类。
解决什么问题,优点。可以解耦日志通知,接口限流,事务管理,如果一直重复实现这些行为,代码会特别冗余,难以维护。
Bean的注入
依赖注入常见三种方式:
- 构造函数注入
- Setter注入
- Field(字段) 注入:在类的字段上使用注解(如 @Autowired 或 @Resource)来注入依赖项。
Bean的实现
- xml:标签和工厂标签
- Java配置类:@Configuration+@Bean手动创建bean逻辑
- 注解:@Component、@Repository、@Service等
xml 和 java配置类 使用场景在于是第三方的SDK、第三方Jar包,不能自己写注解修改的这种场景
注解自然就是自己写的业务代码的逻辑可以用的,同时也是自动装配机制的一环。
自动装配原理
自动装配就是现在的注解时代的不用写xml文件的核心机制。
那这个机制是通过 @SpringBootApplication启动的,而这个注解是三个注解集合:@Configuration、@EnableAutoConfiguration 和 @ComponentScan
- @Configuration: 就是代表配置类,允许在上下文中注册额外的 Bean 或导入其他配置类。
- @EnableAutoConfiguration: 启用 Spring Boot 的自动配置机制。它是自动配置的核心,允许 Spring Boot 根据项目的依赖和配置自动配置 Spring 应用的各个部分。
- @ComponentScan:启用组件扫描。描被 @Component(以及 @Service、@Controller 等)注解的类,并将这些类注册为 Spring 容器中的 Bean。默认情况下,它会扫描该类所在包及其子包下的所有类。
EnableAutoConfiguration是核心,下面有两个核心注解:
@AutoConfigurationPackage,该注解上有一个注解,其中 Registrar 的作用是将启动类所在包下的所有子包的组件扫描注入到spring容器中。这也是为什么在spring boot中,把项目的controller、pojo、service、dao等包放在启动类同级目录下的原因。@Import(AutoConfigurationImportSelector.class):这个注解的有一个方法的作用是去找配置文件,该方法去找META-INF/spring.factories文件中的所有自动配置类,并加载这些类。过程
两条路,第一条路(手动配置的部分,也就是业务代码,有Component)
- 启动:@SpringBootApplication,标记启动类,开始激活装配
扫描:@ComponentScan+@AutoConfigurationPackage,扫描组件
- (范围)@AutoConfigurationPackage:通过注解导入的Registrar类,注册启动类所在的包是基础包,也就是设置了一个默认的扫描最大范围
- (执剑人)@ComponentScan:启动组件扫描器,扫描被@Component等注解修饰的类。
第二条路(自动配置的部分,去找springboot默认自动配置的配置文件配置,)
- 发现:@Import(AutoConfigurationImportSelector.class),发现配置清单(位于META-INF/spring.factories)
- 判断:配置清单上的每一个自动配置的类都会有一个 条件注解,用来判断需不需要装配,比如拿数据库举例子:
判断两个事情:
第一是判断有没有数据库的jar包,就是项目用没用数据库;
第二是判断现在项目里有没有DataSource这个配置类,有说明不用自动装配了,已经在第一条路装配了。没有就需要自动装配。 - 注册:这步是把配置类内部的@Bean方法转化成BeanDefiniton注册到Spring容器里面 ----- 这些BeanDefiniton在生命周期里面实例化成Bean实例。
循环依赖问题
出现的原因:一个类初始化的时候需要另一个类的实例,反过来也是。
怎么解决:提前暴露→在A实例化完但是还没依赖注入(属性填充)的时候,直接把A暴露出来给B用
IOC容器里面维护了三个Map存储不同阶段的Bean实例
| Map名称 | 存储内容 | 作用 | |
|---|---|---|---|
| 一级缓存 | singletonObjects | 存储完整、初始化完毕的单例Bean。(bean) | 正常的Bean容器。 |
| 二级缓存 | earlySingletonObjects | 存储提前暴露的Bean实例,可能是代理对象或原始对象。(Bean) | 存放已暴露但尚未完全初始化的Bean。 |
| 三级缓存 | singletonFactories | 存储一个工厂对象 (逻辑) | 负责判断要不要代理并且返回给二级缓存实例 |
最开始都在三级。进到a和b开始循环依赖的时候,a会判断需不需要AOP代理,需不需要a都会去二级,然后b就能拿到这个半成品的a,完成初始化。死锁就解开了。
不能解决的情况:构造器互相注入的;原型模式的bean。
前者不能的原因是通过构造函数注入就意味着这时候**实例化和注入是一个步骤**,因为在java中创建对象唯一的起点就是调用构造函数,调用之后就是实例化,而不能整出来一个空壳。
解决方法是**setter注入、注解注入**
后者不能的原因是三级缓存这个机制只给**单例模式**用。
解决方法是**改成单例 或 手动模拟三级机制**
