AOP

动态代理实现方式

  1. JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
  2. CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
  3. 区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。

    IoC

    Beanfactory

    BeanFactory和ApplicationContext区别

  4. BeanFactory是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。

  5. ApplicationContext应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能。如国际化,访问资源,载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,消息发送、响应机制,AOP等。
  6. BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化。ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化

    Bean生命周期和创建过程

  • 简单

    • 实例化(是一个半成品)
    • 属性赋值(循环依赖)
    • 初始化(成品)
    • 销毁

      Bean的循环依赖

  • A依赖B;B依赖A,单属性填充时都是半成品。Spring提供三级缓存存放成品和半成品及工厂。 ```java public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    / 一级缓存:单例池 存放已经初始化的bean——成品 */ private final Map singletonObjects = new ConcurrentHashMap(256); / 三级缓存:单例工厂的高速缓存 存放生成bean的工厂 / private final Map> singletonFactories = new HashMap(16); /** 二级缓存:早期单例对象的高速缓存 存放已经实例化但未初始化(未填充属性)的的bean——半成品 / private final Map earlySingletonObjects = new HashMap(16); }

  1. - 缓存,各自存入半成品
  2. 1. getBean方法肯定不陌生,必经之路,然后调用doGetBean,进来以后首先会执行transformedBeanName找别名,看你的Bean上面是否起了别名。然后进行很重要的一步,getSingleton,这段代码就是从你的单例缓存池中获取Bean的实例。那么你第一次进来肯定是没有的,缓存里肯定是拿不到的。也就是一级缓存里是没有的。那么它怎么办呢?他会尝试去二级缓存中去拿,但是去二级缓存中拿并不是无条件的,首先要判断isSingletonCurrentlyInCreation(beanName)他要看你这个对象是否正在创建当中,如果不是直接就退出该方法,如果是的话,他就会去二级缓存earlySingletonObjects里面取,如果没拿到,它还接着判断allowEarlyReference这个东西是否为true。它的意思是说,是否允许让你从单例工厂对象缓存中去拿对象。默认为true。好了,此时如果进来那么就会通过singletonFactory.getObject()去单例工厂缓存中去拿。然后将缓存级别提升至二级缓存也就早期暴露的缓存。
  3. 1. getSingleton执行完以后会走dependsOn方法,判断是否有dependsOn标记的循环引用,有的话直接卡死,抛出异常。比如说A依赖于BB依赖于A 通过dependsOn注解去指定。此时执行到这里就会抛出异常。这里所指并非是构造函数的循环依赖。
  4. 1. beforeSingletonCreation在这里方法里。就把你的对象标记为了早期暴露的对象。提前暴露对象用于创建Bean的实例。
  5. 1. 紧接着就走创建Bean的流程开始。在创建Bean之前执行了一下resolveBeforeInstantiation。它的意思是说,代理AOPBean定义注册信息但是这里并不是实际去代理你的对象,因为对象还没有被创建。只是代理了Bean定义信息,还没有被实例化。把Bean定义信息放进缓存,以便我想代理真正的目标对象的时候,直接去缓存里去拿。
  6. 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创建成功了。那么会走后面的逻辑。
  7. 1. 将创建好的Bean放入缓存,addSingletonFactory方法就是将你创建好的Bean放入三级缓存中。并且移除早期暴露的对象。
  8. 1. 通过populateBean给属性赋值,我们知道,创建好的对象,并不是一个完整的对象,里面的属性还没有被赋值。所以这个方法就是为创建好的Bean为它的属性赋值。并且调用了我们实现的的XXXAware接口进行回调初始化,。然后调用我们实现的Bean的后置处理器,给我们最后一次机会去修改Bean的属性。
  9. ![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)
  10. <a name="tfaNT"></a>
  11. ### Spring后置处理器
  12. 1. BeanPostProcessorBean的后置处理器,主要在bean初始化前后工作。
  13. 1. InstantiationAwareBeanPostProcessor:继承于BeanPostProcessor,主要在实例化bean前后工作; AOP创建代理对象就是通过该接口实现。
  14. 1. BeanFactoryPostProcessorBean工厂的后置处理器,在bean定义(bean definitions)加载完成后,bean尚未初始化前执行。
  15. 1. BeanDefinitionRegistryPostProcessor:继承于BeanFactoryPostProcessor。其自定义的方法postProcessBeanDefinitionRegistry会在bean定义(bean definitions)将要加载,bean尚未初始化前真执行,即在BeanFactoryPostProcessorpostProcessBeanFactory方法前被调用。
  16. <a name="pROEw"></a>
  17. ### 事务隔离级别
  18. <a name="cLnK2"></a>
  19. ### Spring如何解决循环依赖
  20. - Spring使用了**三级缓存解决了循环依赖的问题**。在populateBean()给属性赋值阶段里面Spring会解析你的属性,并且赋值,当发现,A对象里面依赖了B,此时又会走getBean方法,但这个时候,你去缓存中是可以拿的到的。因为我们在对createBeanInstance对象创建完成以后已经放入了缓存当中,所以创建B的时候发现依赖A,直接就从缓存中去拿,此时B创建完,A也创建完,一共执行了4次。至此Bean的创建完成,最后将创建好的Bean放入单例缓存池中。
  21. <a name="lOJJN"></a>
  22. ### 事务传播机制
  23. 1. REQUIRED(默认):支持使用当前事务,如果当前事务不存在,创建一个新事务。required
  24. 1. SUPPORTS:支持使用当前事务,如果当前事务不存在,则不使用事务。supports
  25. 1. MANDATORY:强制,支持使用当前事务,如果当前事务不存在,则抛出Exceptionmandayury
  26. 1. REQUIRES_NEW:创建一个新事务,如果当前事务存在,把当前事务挂起。requires_new
  27. 1. NOT_SUPPORTED:无事务执行,如果当前事务存在,把当前事务挂起。not_support
  28. 1. NEVER:无事务执行,如果当前有事务则抛出Exceptionnever
  29. 1. NESTED:嵌套事务,如果当前事务存在,那么在嵌套的事务中执行。如果当前事务不存在,则表现跟REQUIRED一样。nested
  30. <a name="p2hbL"></a>
  31. ### 循环依赖
  32. <a name="kfu7K"></a>
  33. #### 什么是循环依赖?
  34. ![](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)
  35. - 循环依赖分三种:自身依赖自身、互相依赖、多组循环依赖。本质是创建依赖的依赖创建,无法解耦导致依赖创建失败。
  36. - Spring提供了除构造函数注入和原型注入外的setter循环依赖解决方案。之所以能解决首先是解耦,让类的创建和属性的填充分离,先创建半成品Bean,再处理属性的填充,完成Bean的提供。
  37. ```java
  38. psvm(
  39. new A();
  40. )
  41. class A{
  42. private bb = new B();
  43. }
  44. class B{
  45. private aa = new A();
  46. }
  47. //循环依赖,报错java.lang.StackOverflowError

Spring中的解决(三级缓存)

  • 三级缓存其实是三个Map

    三级缓存(三个Map,)

  1. 第一级缓存:单例缓存池singletonObjects(日常获取Bean的地方)
  2. 第二级缓存:早期提前暴露的对象缓存earlySingletonObjects(还没有属性注入,由三级缓存放进来)。(属性还没有值对象也没有被初始化。K是BeanName,V是Bean对象还没做完属性注入)
  3. 第三级缓存:singletonFactories单例对象工厂缓存(Value 是一个对象工厂;K是BeanName,V是ObjectFactory)。

框架--Spring - 图1

一级缓存可吗?

框架--Spring - 图2

  • 因A的成品创建依赖B,B的成品又依赖A,当需要补全B的时候A还没有创建完,所以会出现死循环。
  • 以及缓存无法拆分,复杂度增加,半成品对象可能存在空指针异常。

框架--Spring - 图3

  • 无法保证多线程下的一级缓存Bean的完整性。

    二级缓存可吗?

    框架--Spring - 图4

  • A创建半成品对象后放到缓存中,接下来补充A对象中依赖B的属性。

  • B继续创建,创建的半成品同样放到缓存中,在补充对象的A属性时,可从半成品缓存中获取,现在B是一个完整对象了,接下来的递归操作一样A也是一个完整对象了。
  • 框架--Spring - 图5
  • 二级缓存完美解决循环依赖+多线程读取不完整Bean的性能问题。但是出现新问题,若A使用了AOP,它需要创建动态代理对象,但是创建动态代理对象是在初始化。
  • 可加缓存,将创建proxy的逻辑放在缓存中,加一个函数式接口。
  • 初始化完成后,把二级缓存remove,塞到一级缓存。我们去getBean的时候,世家拿到的是一级缓存。
  • 框架--Spring - 图6

    三级缓存解决了什么?

    框架--Spring - 图7

  • 三级缓存主要解决Spring AOP的特性,AOP本身就是对方法的增强,ObjectFactory<?> 类型的lambda表达式,而Spring的原子又不希望将此类型的Bean前置创建,所以放到三级缓存中处理。

  • 三级缓存的Value是ObjectFactory,可从里边拿到代理对象。二级缓存的存在是为了性能,从三级缓存的工厂创建对象,再扔到二级缓存(这样就不用每次都要从工厂里拿)。
  • 整体过程类似,唯独是B在填充属性A时,先查询成品缓存、再查询半成品缓存,最后再看有没有单例工程类在三级缓存中。最终获取到以后调用getObject方法返回代理引用或原始引用。
  • 框架--Spring - 图8

    总结

  • 一级缓存可解决循环依赖,但是性能低、耦合性扩展性都很差。

  • 二级缓存中AOP动态代理会重复创建。

    为什么需要二级缓存

  • 和一级缓存对比,二级缓存式为了分离成熟Bean和纯净Bean(未注入属性)的存放,防止多线程中在Bean还未创建完成时读取到的Bean不完整。所以二级为了保证getBean是完整最终的Bean。

    三级缓存下二级缓存的作用?
  • 为了存储三级缓存创建出来的早期Bean(有可能也是代理对象),为了避免三级缓存重复执行。

    为什么需要三级缓存

  • Bean的AOP动态代理创建是在初始化之后,但是循环依赖的Bean若使用了AOP,无法等到解决完循环依赖再创建动态代理,因为这个时候以及注入属性,所以若循环依赖的Bean使用了AOP,需要提前创建aop。

    为什么Spring不能解决构造器的循环依赖?

  • 再Bean调用构造器实例化之前,一二三级缓存并没有Bean的任何相关信息,在实例化之后才放入三级缓存中,因此当getBean的时候缓存并没有命中,这样就抛出了循环依赖异常了。

    为什么多例无法解决循环依赖

  • 单例可利用缓存setter注入避免了使用构造器,原型无法利用缓存。

框架--Spring - 图9

多线程怎么避免获取到不完整的Bean。

框架--Spring - 图10


类加载机制

  • 加载 生产class对象
  • (验证(格式)、准备(默认值)、解析)
  • 初始化(先初始化父类)

    类加载器

  • 启动类

  • 扩展
  • 应用

    双亲委派

  • 避免重复加载