SpringBean 生命周期
Spring 循环依赖
- 先说结论,三级缓存,提前暴露对象,aop,解决循环依赖
- 问题:IOC容器中对象A 依赖B, B依赖A
- 先说Bean创建过程,如上图:实例化 + 初始化(填充属性)
- 先创建对象A,实例化A,此时A对象中B属性为空,填充B属性
- 从容器中查找B对象,如果找到B直接赋值不存在循环依赖,如果找不到,直接创建B属性
- 实例化B,此时B中A为空,填充A属性
- 从容器中查找A对象,找不到直接创建
- 形成依赖闭环
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//二级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}
一级缓存 二级缓存 解决普通 对象循环依赖
- 解决问题的方式
- 将没有初始化的对象A提前暴露,相当于提前暴露某个不完整对象的引用
- 因此解决的核心在于: 实例化 (createBeanInstance)和 初始化(populateBean)分开操作,也就能解决循环依赖问题
- 为什么要多级缓存:
- 安提前暴露做法,容器中会存在多种状态: 实例化完成-初始化未完成, 完整状态,因此要用不同的map结构来存储,因此又了一级缓存,二级缓存
- 如果一级缓存中有对象,二级缓存中不会存在同名对象,查找顺序1,2,3,一级缓存中存放完整对象,二级缓存存放非完整对
三级缓存解决代理对象循环依赖
-
三级缓存是存储的 ObjectFactory,是一个函数式接口,存在的意义在于 保证整个容器运行过程中同名的bean对象只有一个
-
问题:如果一个对象需要被代理,或者需要生成代理对象,那么要不要先生成普通对象:答案是要的
- 普通对象和代理对象不能同时在容器中,因此当对象需要被代理时候,就使用代理对象覆盖之前的普通对象,实际使用中是没有办法确认对象什么时候使用,因此会在三级缓存中是传入的一个lambada表达式getEarlyBeanReference(beanName, mbd, bean)
- getEarlyBeanReference(beanName, mbd, bean) 作用是判断对象是否需要被代理,类似一种回调机制实现,以此来实现代理对象覆盖已有普通对象
-
如上:所有bean对象创建时候 优先放入三级缓存中,在后续使用过程中,如果需要被代理则返回代理对象,如果不需要代理,直接返回普通对象。
缓存放置时间和删除时间
- 三级缓存:createBeanInstance之后: addSingletonFactory
- 二级缓存:第一次从三级缓存确认对象是代理对象还是普通对象时候,同时删除三级缓存 getSingleton
- 一级缓存:生成完整对象之后放入一级缓存,删除二三级缓存:addSingleton
Bean Factory 与Factory Bean区别
- 相同点:都是创建Bean对象用的
- 不同点:
- 使用BeanFactory创建对象时候,必须要遵循严格的Bean生命周期流程,太复杂
- 要简单创建自定义对象,同时交给Spring管理,也可以实现FactoryBean接口,有如下三个方法
- isSingleton:是否单例对象
- getObjectType:通过类型查找对象
- getObject:自定义创建对象的过程(new,反射,动态代理)
Spring中用到的设计模式
- 单例模式:bean默认都是单例的
- 工厂模式:BeanFactory
- 模板方法模式:onRefresh:onRefresh()是模板方法,具体的子类可以在这里初始化一些特殊的 Bean,比如在SPringBoot中我们通过子类实现onRefresh
- 策略模式:xmlBeanDefinitionReader
- 适配器模式:adapter
- 装饰器模式:BeanWrapper
- 代理模式:动态代理
BeanFactory 与 ApplicationContet的理解
- BeanFactory是SpringIOC容器中定义一个基础实现接口,其中他定义了获取Bean对象的基础方法,比如getBean,isSingleton
- ApplicationContext是Bean的应用上下文,它不仅仅是BeanFactroy的一个子类,因为BeanFactory的子类LinstableBeanFactory,同时ApplicationContext是LinstableBeanFactory的一个子类,它通过这种方式得到了获取Bean的一颗方法,并且他还有通过继承的方式获得了构建Bean的一些其他的功能,比如ResourceLoader
- ResourceLoader中定义了我们文件定位,获取,解析的一些功能,在我们用FileSystemXmlApplicationContext去加载Bean的时候,我们会给一个Xml文件的路径作为参数,此处对Xml文件的获取就是通过ResourceLoader中getPath方法去找到的,之后通过ResourceLoader中持有的文件InputStream文件流信息的解析得到我们的Documnt对象,这个Documernt对象就是我们在Spring的配置文件中配置的标签对信息。接着我们通过DocumentDefinitionReader来对Documernt对象进行解析,最终得到一个BeanDefinition对象,这个对象是SpringIOC容器中对Bean定义的一个基础数据结构,也可以说是元数据信息。这所有的步骤都是在ApplicationContext提供的功能以及上下文中完成的Bean的初始化工作。
BeanFactoryPostProcesseor理解
- BeanFactoryPostProcesseor是在BeanFactory创建完成之后的一个后置处理器,他是Spring中的一个典型的扩展点,他可以让用户自定义在BeanFactory完成创建后可以做一些我们自定义的逻辑已此达到一些业务上的实现
- 在Spring中也有部分功能是通过这个扩展点来实现的,比如说,@Configuration注解的一个解析的流程并不是在创建BeanFactory的时候做的,而是在后置处理器中完成的对@Configuration的一个解析流程,通过递归的方式加载@Configuration修饰的类中定义的Bean,将各个Bean解析成BeanDefinition之后添加到BeanFactory中。
BeanPostProcessor的理解
- BeanPostProcessor是 Spring定义的一个接口,其中定义了两个方法,postProcessBeforeInitialization 和postProcessAfterInitialization ,他在Bean的整个创建生命周期的一个典型扩展点,是对Bean的一个后置增强处理的过程
- Spring会在PostProcessorRegistrationDelegate中扫描所有实现了BeanPostProcessor接口的对象,并且将他保存到List中,并且Spring在创建Bean对象过程中,会去遍历list中的process,按照一定顺序执行process方法中逻辑对Bean对做一个修改
- 因此我们可以通过实现BeanPostProcess的方式来做到在Bean生成之后,在postProcessAfterInitialization中定义自己的一个业务逻辑来对Bean进行指定的改动以此达到我们的业务上的一些目标。
Import 注解的理解
- Import注解是Spring3.0 的之后加入的,他的目的是为了替换Xml文件中import标签,用来倒入第三方的java类到容器中
- Import还有其他的扩展功能,不仅仅可以导入java的配置类,还可以将某个类型的对象注入到容器中
- 比如,如果我们导入的对象实现了ImportSelector接口,这个接口中有一个selectimports方法,他返回的是一个数组,那么Import标签会加载这个数组中所有的全限定明表示的类,并且注册到容器中。
SpringBoot自动装配原理
- 在SpringBoot项目启动的时候,我们需要在启动类上增加一个注解SpringBootApplication
- 在SpringBootApplication注解中包含了其他的注解,其中EnableAutoConfiguration是和自动装配相关的一个注解
- 在EnableAutoConfiguration注解中他通过Import注解的方式在完成了自动的注入,在IMport注解中传入的是AutoConfigurationSelectImport他是SelectImpot的一个实现类,最终获取配置的方法是在importSelecter实现方法中,他定义了我们去读取改报目录下META-INFO/目录下的Spring.factories中的配置信息
- 在加载完配置细腻后经过去重,筛选,后返回给IMport注解一个数组对象,里面就是我们要启动的时候加载到IOC容器中的所有的类,
- 到此完成了SpringBoot的自动装配过程。
Spring事务传播行为
- Spring事务传播行为是针对嵌套事务的一个策略,有如下7个
事务行为 | 说明 |
---|---|
PROPAGATION_REQUIRED | 支持当前事务,如果没有,新建一个事物 |
PROPAGATION_SUPPORTS | 支持当前事务,如果没有,就以非事务方式运行 |
PROPAGATION_MANDATORY | 支持当前事务,如果没有,抛异常 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,就将当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务的方式运行,如果当前存在事务,就挂起 |
PROPAGATION_NEVER | 以非事务方式运行,如果当前存在事务,则抛异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。 如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似操作 |
- 案例说明
ServiceA {
void methodA() {
ServiceB.methodB();
}
}
ServiceB {
void methodB()
{
}
}
-
PROPAGATION_REQUIRED 模式:支持当前事务,如果没有事务,则新建一个
-
如果ServerA执行的时候,如果发现自己没在事务中,他会自己分配一个事物
-
如果ServerA执行的时候以及启了事务,这个时候调用B,那么B就不会新启事务
-
那么会有如下流程:
- 执行B,失败,则直接失败就行,A还没开始
- 执行B,成功,继续执行A,A成功都成功,A失败A,B都回滚,都在A事务中完成回滚。
Spring事务实现方式
- 编程式事务管理:通过Spring中提供的一些API的方式来管理事务,比如自己定义事务,开启,提交,回滚等操作,自己在方法中手动调用完成
- 申明式事务管理:将事务管理和业务代码分离,值通过注解或者Xml的方式配置事务管理
事务注解的本质
- @Transactional 注解作用有两个:
- 定义被修饰的方法是要参与事务的方法
- 配置相关属性来定制事务的参与方式与运行方式。比如定义事务隔离级别默认default,以及定义事务传播行为等属性
Spring 中非事务方法A调用事务方法B,事务失败
- 采用AopContext.currentProxy().B()来进行调用,事务才能生效。因为AopContext.currentProxy() 使用来ThreadLocal保存了代理对象,因此AopContext.currentProxy().B()就能生效
- 在直接A调用B的时候,我们使用的是对象B而不是代理生成的对象B,所以对B的切入没有生效
Spring MVC 请求执行流程
- 客户发发送请求
- 请求到后段后,先通过DispacherServlet 接收请求,分发请求,处理响应
- 通过HanlderMapping 中依据请求对应的URL找到我们配置的一个Handler信息
- 接着找到Handler后执行HandlerAdapter代理,作用就是调用对应的Controller
- 执行我们自实现的Controller来完成业务逻辑
- 通过ViewResolver来执行结果的封装后返回一个ModleAndView对象给前端
Spring 与SpringMVC的关系
- Spring 与Spring MVC整合的项目中,存在三级对象的关系,Controller,Service, Dao,这三个都有对应的注解来表示,例如 @Controller @Server @Repository,三个注解都是对Bean的注入操作,但是正对的事不同角色而已
- 在Spring与SpringMvc中都有自定定义的一个IOC容器
- Controller中定义的实例都是由于SpringMVC定义的IOC容器管理
- Service 与Dao中定义的实例是由于Spring 的IOC容器管理
- 两个容器具有一个父子关系,SpringIOC容器是SpringMVCIOC容器的父容器,因此我们能在SpringMVC的容器中获取到父容器SpringIOC容器中的Service信息。
Spring Boot 中Starter原理
- Spring boot 中Starter的设计核心思想是通过一种便捷的方式来完成Spring依赖的自动配置
- 每一个Spring Boot 的Starter都是一个普通的Maven项目,在对于的项目达成jar包后,在他的MATE-INFO目录底下都有一个Spring.factory的配置文件,这个配置文件中包含了本Starter功能所需要的所有的Bean对象的全限定名信息
- Spring Boot 通过自动装配机制来完成对这个spring.factory配置文件中所有Bean对象的一个依赖注入的过程(说一下自动装配原理)
- 依此中形式达到组件引入0 配置的目的