AOP
动态代理实现方式
- JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
- CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。
IoC
Beanfactory
BeanFactory和ApplicationContext区别
BeanFactory是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。
- ApplicationContext应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能。如国际化,访问资源,载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,消息发送、响应机制,AOP等。
- BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化。ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化
Bean生命周期和创建过程
简单
A依赖B;B依赖A,单属性填充时都是半成品。Spring提供三级缓存存放成品和半成品及工厂。 ```java public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/ 一级缓存:单例池 存放已经初始化的bean——成品 */ private final MapsingletonObjects = new ConcurrentHashMap(256); / 三级缓存:单例工厂的高速缓存 存放生成bean的工厂 / private final Map> singletonFactories = new HashMap(16); /** 二级缓存:早期单例对象的高速缓存 存放已经实例化但未初始化(未填充属性)的的bean——半成品 / private final MapearlySingletonObjects = new HashMap(16); }
- 缓存,各自存入半成品
1. getBean方法肯定不陌生,必经之路,然后调用doGetBean,进来以后首先会执行transformedBeanName找别名,看你的Bean上面是否起了别名。然后进行很重要的一步,getSingleton,这段代码就是从你的单例缓存池中获取Bean的实例。那么你第一次进来肯定是没有的,缓存里肯定是拿不到的。也就是一级缓存里是没有的。那么它怎么办呢?他会尝试去二级缓存中去拿,但是去二级缓存中拿并不是无条件的,首先要判断isSingletonCurrentlyInCreation(beanName)他要看你这个对象是否正在创建当中,如果不是直接就退出该方法,如果是的话,他就会去二级缓存earlySingletonObjects里面取,如果没拿到,它还接着判断allowEarlyReference这个东西是否为true。它的意思是说,是否允许让你从单例工厂对象缓存中去拿对象。默认为true。好了,此时如果进来那么就会通过singletonFactory.getObject()去单例工厂缓存中去拿。然后将缓存级别提升至二级缓存也就早期暴露的缓存。
1. getSingleton执行完以后会走dependsOn方法,判断是否有dependsOn标记的循环引用,有的话直接卡死,抛出异常。比如说A依赖于B,B依赖于A 通过dependsOn注解去指定。此时执行到这里就会抛出异常。这里所指并非是构造函数的循环依赖。
1. beforeSingletonCreation在这里方法里。就把你的对象标记为了早期暴露的对象。提前暴露对象用于创建Bean的实例。
1. 紧接着就走创建Bean的流程开始。在创建Bean之前执行了一下resolveBeforeInstantiation。它的意思是说,代理AOPBean定义注册信息但是这里并不是实际去代理你的对象,因为对象还没有被创建。只是代理了Bean定义信息,还没有被实例化。把Bean定义信息放进缓存,以便我想代理真正的目标对象的时候,直接去缓存里去拿。
1. 接下来就真正的走创建Bean流程,首先走进真正做事儿的方法doCreateBean然后找到createBeanInstance这个方法,在这里面它将为你创建你的Bean实例信息(Bean的实例)。如果说创建成功了,那么就把你的对象放入缓存中去(将创建好的提前曝光的对象放入singletonFactories三级缓存中)将对象从二级缓存中移除因为它已经不是提前暴露的对象了。但是。如果说在createBeanInstance这个方法中在创建Bean的时候它会去检测你的依赖关系,会去检测你的构造器。然后,如果说它在创建A对象的时候,发现了构造器里依赖了B,然后它又会重新走getBean的这个流程,当在走到这里的时候,又发现依赖了A此时就会抛出异常。为什么会抛出异常,因为,走getBean的时候他会去从你的单例缓存池中去拿,因为你这里的Bean还没有被创建好。自然不会被放进缓存中,所以它是在缓存中拿不到B对象的。反过来也是拿不到A对象的。造成了死循环故此直接抛异常。这就是为什么Spring IOC不能解决构造器循环依赖的原因。因为你还没来的急放入缓存你的对象是不存在的。所以不能创建。同理@Bean标注的循环依赖方法也是不能解决的,跟这个同理。那么多例就更不能解决了。为什么?因为在走createBeanInstance的时候,会判断是否是单例的Bean定义信息mbd.isSingleton();如果是才会进来。所以多例的Bean压根就不会走进来,而是走了另一段逻辑,这里不做介绍。至此,构造器循环依赖和@Bean的循环依赖还有多例Bean的循环依赖为什么不能解决已经解释清楚。然后如果说,Bean创建成功了。那么会走后面的逻辑。
1. 将创建好的Bean放入缓存,addSingletonFactory方法就是将你创建好的Bean放入三级缓存中。并且移除早期暴露的对象。
1. 通过populateBean给属性赋值,我们知道,创建好的对象,并不是一个完整的对象,里面的属性还没有被赋值。所以这个方法就是为创建好的Bean为它的属性赋值。并且调用了我们实现的的XXXAware接口进行回调初始化,。然后调用我们实现的Bean的后置处理器,给我们最后一次机会去修改Bean的属性。
![image.png](https://cdn.nlark.com/yuque/0/2021/png/12844611/1625726175262-c71fa500-e358-42d3-ac72-10b5285af265.png#height=423&id=y52re&margin=%5Bobject%20Object%5D&name=image.png&originHeight=845&originWidth=1748&originalType=binary&ratio=1&size=133038&status=done&style=none&width=874)
<a name="tfaNT"></a>
### Spring后置处理器
1. BeanPostProcessor:Bean的后置处理器,主要在bean初始化前后工作。
1. InstantiationAwareBeanPostProcessor:继承于BeanPostProcessor,主要在实例化bean前后工作; AOP创建代理对象就是通过该接口实现。
1. BeanFactoryPostProcessor:Bean工厂的后置处理器,在bean定义(bean definitions)加载完成后,bean尚未初始化前执行。
1. BeanDefinitionRegistryPostProcessor:继承于BeanFactoryPostProcessor。其自定义的方法postProcessBeanDefinitionRegistry会在bean定义(bean definitions)将要加载,bean尚未初始化前真执行,即在BeanFactoryPostProcessor的postProcessBeanFactory方法前被调用。
<a name="pROEw"></a>
### 事务隔离级别
<a name="cLnK2"></a>
### Spring如何解决循环依赖
- Spring使用了**三级缓存解决了循环依赖的问题**。在populateBean()给属性赋值阶段里面Spring会解析你的属性,并且赋值,当发现,A对象里面依赖了B,此时又会走getBean方法,但这个时候,你去缓存中是可以拿的到的。因为我们在对createBeanInstance对象创建完成以后已经放入了缓存当中,所以创建B的时候发现依赖A,直接就从缓存中去拿,此时B创建完,A也创建完,一共执行了4次。至此Bean的创建完成,最后将创建好的Bean放入单例缓存池中。
<a name="lOJJN"></a>
### 事务传播机制
1. REQUIRED(默认):支持使用当前事务,如果当前事务不存在,创建一个新事务。required
1. SUPPORTS:支持使用当前事务,如果当前事务不存在,则不使用事务。supports
1. MANDATORY:强制,支持使用当前事务,如果当前事务不存在,则抛出Exception。mandayury
1. REQUIRES_NEW:创建一个新事务,如果当前事务存在,把当前事务挂起。requires_new
1. NOT_SUPPORTED:无事务执行,如果当前事务存在,把当前事务挂起。not_support
1. NEVER:无事务执行,如果当前有事务则抛出Exception。never
1. NESTED:嵌套事务,如果当前事务存在,那么在嵌套的事务中执行。如果当前事务不存在,则表现跟REQUIRED一样。nested
<a name="p2hbL"></a>
### 循环依赖
<a name="kfu7K"></a>
#### 什么是循环依赖?
![](https://cdn.nlark.com/yuque/0/2021/jpeg/12844611/1626154416457-d1834acd-9022-4829-b304-61b91961ca90.jpeg#height=500&id=jITxD&originHeight=500&originWidth=1396&originalType=binary&ratio=1&size=0&status=done&style=none&width=1396)
- 循环依赖分三种:自身依赖自身、互相依赖、多组循环依赖。本质是创建依赖的依赖创建,无法解耦导致依赖创建失败。
- Spring提供了除构造函数注入和原型注入外的setter循环依赖解决方案。之所以能解决首先是解耦,让类的创建和属性的填充分离,先创建半成品Bean,再处理属性的填充,完成Bean的提供。
```java
psvm(
new A();
)
class A{
private bb = new B();
}
class B{
private aa = new A();
}
//循环依赖,报错java.lang.StackOverflowError
Spring中的解决(三级缓存)
- 第一级缓存:单例缓存池singletonObjects(日常获取Bean的地方)。
- 第二级缓存:早期提前暴露的对象缓存earlySingletonObjects(还没有属性注入,由三级缓存放进来)。(属性还没有值对象也没有被初始化。K是BeanName,V是Bean对象还没做完属性注入)
- 第三级缓存:singletonFactories单例对象工厂缓存(Value 是一个对象工厂;K是BeanName,V是ObjectFactory)。
一级缓存可吗?
- 因A的成品创建依赖B,B的成品又依赖A,当需要补全B的时候A还没有创建完,所以会出现死循环。
- 以及缓存无法拆分,复杂度增加,半成品对象可能存在空指针异常。
-
二级缓存可吗?
A创建半成品对象后放到缓存中,接下来补充A对象中依赖B的属性。
- B继续创建,创建的半成品同样放到缓存中,在补充对象的A属性时,可从半成品缓存中获取,现在B是一个完整对象了,接下来的递归操作一样A也是一个完整对象了。
- 二级缓存完美解决循环依赖+多线程读取不完整Bean的性能问题。但是出现新问题,若A使用了AOP,它需要创建动态代理对象,但是创建动态代理对象是在初始化。
- 可加缓存,将创建proxy的逻辑放在缓存中,加一个函数式接口。
- 初始化完成后,把二级缓存remove,塞到一级缓存。我们去getBean的时候,世家拿到的是一级缓存。
-
三级缓存解决了什么?
三级缓存主要解决Spring AOP的特性,AOP本身就是对方法的增强,是
ObjectFactory<?>
类型的lambda表达式,而Spring的原子又不希望将此类型的Bean前置创建,所以放到三级缓存中处理。- 三级缓存的Value是ObjectFactory,可从里边拿到代理对象。二级缓存的存在是为了性能,从三级缓存的工厂创建对象,再扔到二级缓存(这样就不用每次都要从工厂里拿)。
- 整体过程类似,唯独是B在填充属性A时,先查询成品缓存、再查询半成品缓存,最后再看有没有单例工程类在三级缓存中。最终获取到以后调用getObject方法返回代理引用或原始引用。
-
总结
一级缓存可解决循环依赖,但是性能低、耦合性扩展性都很差。
-
为什么需要二级缓存
和一级缓存对比,二级缓存式为了分离成熟Bean和纯净Bean(未注入属性)的存放,防止多线程中在Bean还未创建完成时读取到的Bean不完整。所以二级为了保证getBean是完整最终的Bean。
三级缓存下二级缓存的作用?
为了存储三级缓存创建出来的早期Bean(有可能也是代理对象),为了避免三级缓存重复执行。
为什么需要三级缓存
Bean的AOP动态代理创建是在初始化之后,但是循环依赖的Bean若使用了AOP,无法等到解决完循环依赖再创建动态代理,因为这个时候以及注入属性,所以若循环依赖的Bean使用了AOP,需要提前创建aop。
为什么Spring不能解决构造器的循环依赖?
再Bean调用构造器实例化之前,一二三级缓存并没有Bean的任何相关信息,在实例化之后才放入三级缓存中,因此当getBean的时候缓存并没有命中,这样就抛出了循环依赖异常了。
为什么多例无法解决循环依赖
单例可利用缓存setter注入避免了使用构造器,原型无法利用缓存。