孙哥Spring源码第9集

第9集 bean标签的属性填充再分析,@Autowired @Value注解形式的注入

【视频来源于:B站up主孙帅suns Spring源码视频】【微信号:suns45】

9.1 上节回顾

作为整个spring创建对象的流程来讲,实际上包含若干个步骤

1、【第一个步骤就是getBean,是我们最开始调用的。】

2、【doGetBean【交由另外一个方法doGetBean完成后续实质性的工作内容】】

【doGetBean包含若干个环节包括对象的获取和对象的创建】

【这两个部分分别是对象的获取还有对象的创建 由这两部分构成的】

【对象的获取就是从相应的各种Map中获取这个对象,还包括父子容器】

【如果获取不到就会走下面的这个流程就是我们所说的这个对象的创建】

【对象的创建核心交给另外一个方法帮他完成相应的工作,这个方法叫做createBean来完成】

【createBean后续会交给doCreateBean】

【而doCreateBean包括几个内容,1、创建对象,2、属性填充【主要分析的是set,自动注册平常很少涉及到,在整个的属性填充实际上用的是set注入,3、初始化就是最后一个工作】

3、【到上次课为止主要讲的是属性的填充】

​ 【属性的填充实际上 就是通过set 为这个bean 存储相应属性的值】

​ 【分为两大类,基于bean标签的处理,基于注解的形式】

​ 【注解指的是什么注解?@Autowired @Value】

【下面讲解的内容主要就是bean标签的属性填充再分析,注解形式@Autowired @Value是如何完成的】

【看代码】

9.2 populateBean-applyPropertyValues分析

9.2.1 TypedStringValue类型处理过程

  • 1、找到populateBean,其中最核心的关于xml注入的就是applyPropertyValues()【处理bean标签的注入】
  • 2、遍历原始的属性类型,找到属性名,属性值,以及解析后的类型,解析后的类型这块会涉及到一个核心的解析过程【resolveValueIfNecessary获取转换器的一个过程】,最后会在convertForProperty完成一个实质性的转换。
  • 3、在转化过程当中properties的value对应的类型实际的类型是TypedStringValue,后续通过内置转换器或者是自定义的类型转换器最终完成类型转换,实际上我们熟悉的bean标签的set注入的这种情况,除了我们说的JDK类型,还有一种set注入的方式类型,就是用户定义的类型,在properties value设置一个自定义类 注意引用使用ref,如果一个类涉及到了自定义类型,具体在源码当中,是如何体现类型转化工作的?

9.2.2 RuntimeBeanReferences自定义类型处理过程

  • 在User类中,定义一个Address类成员变量,在配置文件中bean Address value… ref… 引入Address类,在测试类中获取user,address一定会进行注入

    getName获得的是属性的名字【propertyName,是address这个string值】RuntimeBeanReferences

  • getValue获取的是属性原始的值【resolvedValue,是RuntimeBeanReferences这个类型】

  • JDK类型的是TypedStringValue

  • 自定义注入类型是RuntimeBeanReferences,后续类型转换之后会变成实质性的bean

9.2.2.1 转化位置
  • 转化发生的位置在哪呢?valueResolver.resolveValueIfNecessary(),进入这个方法,此时会发现 它专门处理了RuntimeBeanReferences,猜一下接下来的逻辑应该是怎么样的呢?

  • 如果Spring在处理的过程中发现RuntimeBeanReferencens这个类型,那么spring下面的逻辑应该是什么呢?

  • 在set注入的过程当中 spring一定会 为这个User注入Address对象,一旦分析是RuntimeBeanReferences之后,最终的目的是把RuntimeBeanReferences变成Address对象,怎么能获得这个对象呢?

  • 这个address也是spring工厂当中我们生命的一个bean标签,也是要从工厂获取这个address,

  • 下面的逻辑就是RuntimeBeanReferences的作用就是从工厂中获取Address对象,怎么从工厂中获得对象?beanFactory.getBean() ->doGetBean() ->createBean->doCreateBean这个流程,把获取User的过程重新来一遍。

9.2.2.2 resolveValueIfNecessary解析
  • 继续这个resolveValueIfNecessary方法

  • 1、进入resolveReference这个方法

  • 1.1、拿bean的名字refName

  • 1.2、如果有父亲的话,从父工厂解决问题

  • 1.3、最终的逻辑一定会运行到this.beanFactory.getBean(refName)->doGetBean()…->doCreateBean->单实例对象的那个分支

  • 2、Address和User创建对象相比还有属性 需要set注入,还需要走applyPropertyValues(),Address没有属性,如果有属性还会再走一遍

  • 这就是 用户自定义类型注入的过程。

9.2.3 总结

  • 在创建bean的过程中,进行属性填充,如果是bean标签调用的就是applayPropertyValues这个函数,作为这个函数来讲,基本类型都是TypedStringValue,自定义类型都封装成了RuntimeBeanReference,会走valueResolver.resolveValueIfNecessary()这个流程

  • 基本类型的注入靠的是convertForProperty(resolvedValue, propertyName, bw, converter)这个函数来完成的


9.2 @Autowired @Value的处理

9.2.1 搭建测试

1、首先定义了我的配置类
如果用springboot,可以换成使用springboot 专门处理yaml的注解效果是一样的

孙哥Spring源码第9集_第1张图片

2、对应的配置文件就是app.properties

孙哥Spring源码第9集_第2张图片

3、创建了两个类,关注 Product中 @Value id 和 @Value name

孙哥Spring源码第9集_第3张图片

4、测试

孙哥Spring源码第9集_第4张图片

9.2.2 处理AutowiredAnnotationBeanPostProcessor

孙哥Spring源码第9集_第5张图片
  • 1、通过一个AutowiredAnnotationBeanPostProcessor完成的注入过程。

  • 2、@Autowired @Value 基于AutowiredAnnotationBeanPostProcessor来完成的

  • 3、整个流程体现在什么位置的呢?

    • populateBean的if(hasInstAwareBpps){【打断点】

    • ​ xxxx

    • }

  • 4、打断点查看

    • 在dubug的时候 需要对断点添加 product情况判断

    • 把需要注入的属性PropertyValues拿出来

    • 查看for循环遍历BeanPostProcessor,查看跟AOP相关的都是带Proxy的。

  • 5、AutowiredAnnotationBeanPostProcessor的作用

    • AutowiredAnnotationBeanPostProcessor是真正发挥作用的,这个for循环把子实现一个一个过,此时我们要抓的是AutowiredAnnotationBeanPostProcessor,而ConfigurationClassPostProcessor处理的是@Configuration,CommonAnnotationBeanPostProcessor 处理的是@Resource @PostConstruct @PreDestory
      spring后续给定的所有注解 都是通过BeanProcessor来完成的

9.2.3 AutowiredAnnotationBeanPostProcessor处理过程

孙哥Spring源码第9集_第6张图片
  • 通过ibp.postProcessProperties()的调用来完成@Autowired @Value的注入工作。
孙哥Spring源码第9集_第7张图片
  • 看InjectionMetadata metadata里面就是我们需要注入的内容

  • metadata.inject是真正完成了注解的处理

  • 进到这个方法中

  • 孙哥Spring源码第9集_第8张图片
  • 此时element就是我们属性,遍历的就是我们的属性,拿到一个属性进行一个element.inject()

  • 第一个遍历进去这个方法,发现field就是Product的id

  • 孙哥Spring源码第9集_第9张图片
  • beanFactory.resolveDependency把注解里面的对应的配置文件的值获取出来了,

  • ReflectionUtils.makeAccessible(filed)打破封装

  • field.set()进行赋值。

9.2.4 总结

  • 注解的形式 @Autowired @Value的整个流程就是

  • AutowiredAnnotationBeanPostProcessor

  • ​ postProcessorProperties

  • ​ metadata.inject(bean,beanName,pvs)

  • ​ value=beanFactory.resolveDependency->StringValueResovler ${product.id} 替换成了1

  • ​ filed.set(bean,value)

  • 最后再次debug一下,把断点打到populateBean()方法中,执行完PostProcessor的地方,看wrappedObject属性是否有值。

  • 把@Autowired Acount去掉,发现Account为空,有了@Autowired Account就有值了

9.3 属性填充的总结

不管通过

当如果这个类型自定义的对象时,他一定会继续通过BeanFactory#getBean()

9.4 初始化

9.4.1 初始化的流程

孙哥Spring源码第9集_第10张图片

9.4.2 属性填充中的BeanPostProcessor、初始化中的BeanPostProcessor的区别是什么?

一般情况下 指的都是Spring自己提供的BeanPostProcessor。

初始化过程中的BeanPostProcessor一般都是程序猿提供的。

9.4.3 AOP创建动态代理它是通过什么样的方式来完成

  • Spring基础课程讲过一个概念,AOP创建动态代理它是通过什么样的方式来完成的呢?

  • AOP创建动态代理的底层是 JDK/ Cglib,在spring当中AOP到底是通过什么技术来把动态代理

  • 编程方式 通过切入点 原始对象 组装成切面。

  • AOP最终是通过BeanPostProcessor来完成的

  • 如果你感到有困惑可以去B站找孙帅suns的基础spring课。

  • 最终一定会跟创建对象有关,一旦用了动态代理,最终创建的是个代理对象。

  • 有一个问题,AOP一定会在对象创建中体现,它是用BeanPostProcessor的方式来完成的,刚才也分析了整个创建对象的过程。

  • docreateBean 创建对象实际上创建的实际上 是原始对象,只不过就是对原始对象 Wrapper进行了一个包装。包装的目的是什么?

    • 把类型转换器放到这个类中,他没有创建代理的可能。
    • 那属性的填充和初始化也是创建对象的两个流程哇,你也会发现属性填充里面也使用到了BeanPostProcessor,初始化当中也是用BeanPostProcessor。这里面就会有一个核心问题,AOP创建的代理 是在属性填充中完成的呢? 还是在初始化的时候完成的呢?
    • 两个地方都用了BeanPostProcessor,那么到底是在哪里创建的呢?为什么要讲这个问题呢?
    • 这个问题 直接会影响到 循环引用 理解不到位的问题。
  • 在解决循环引用的时候,它号称使用了3种缓存,也就是所谓的3级缓存,官方没有这个概念,所谓的这个三级缓存在整个spring的体系中没有人提过。提出这个概念的人,应该加一嘴说这是他自己提出来的而不是官方。

  • 这个地方代理创建的时机 最后决定了 怎么解决循环引用,以及为什么循环引用要那么设计?

  • 解决AOP动态代理的BeanPostProcessor到底是在属性填充里面做的,还是在初始化环节做的?

9.5 探究AOP创建的时机

我们现开发代理的环境

1、引入三个依赖

孙哥Spring源码第9集_第11张图片

2、开发一个bean

孙哥Spring源码第9集_第12张图片

3、写一个Before实现一个接口,这和注解的@Before是一样的

孙哥Spring源码第9集_第13张图片

4、配置在配置文件当中。

孙哥Spring源码第9集_第14张图片

5、写代理

孙哥Spring源码第9集_第15张图片

6、测试AOP编程

孙哥Spring源码第9集_第16张图片

如果能在执行register之前执行AOP的打印就证明成功了。

7、查看结果

孙哥Spring源码第9集_第17张图片

8、跟踪代码到创建对象那一行代码下面,根据debug查看值发现没有被代理的

9、跟踪到populateBean()的下一行代码查看instanceWrapper,发现没有创建代理

10、打到initializeBean()的下一行代码,发现exposedObject是代理对象,说明在初始化方法的操作中完成了代理的创建。

9.5.1 代理的创建是在初始化阶段完成,初始化阶段是怎么做的呢?

进入到initalizeBean这个方法中,看是BeanPostProcessorBefore帮我们做的还是 BeanPostProcessorAfter帮我们做的。

通过条件断点定位到BeanPostProcessorBefore后面和BeanPostProcessorAfter后面查看 wrappedBean是否是代理对象

最后发现 是BeanPostProcessorAfter完成了代理对象的创建。

9.5.2 Spring为什么要在初始化过程中进行代理对象的创建呢?

  • spring防止你在初始化过程当中,会做一些额外的处理,为了保证处理的安全,所以在最后来完成代理对象的创建。

  • 进入到applyBeanPostProcessorAfterInitialization方法里面

  • 发现经过for循环的BeanPostProcessor处理之后才会有代理对象的产生。

  • ProxyTargetClass是false说明不是基于类做代理而是基于接口来实现代理的。

  • 孙哥Spring源码第9集_第18张图片
  • 看这个beanPostProcessor是AspectJAwareAdvisorAutoProxyCreator来完成代理的,其实我们说的AOP通过BeanPostProcessor来完成的实际上就是AspectJAwareAdvisorAutoProxyCreator,这个地方后续还是会讲的,当前只是做一个铺垫。

  • 经过这个beanPostProcessor处理完成之后,它应该就是一个代理对象了,可以通过debug代理验证。

  • 查看一下current

    • 孙哥Spring源码第9集_第19张图片

9.5.3 总结

总结:AOP的代理对象 是在初始化阶段中的applyBeanPostProcessorAfterInitialization完成的,这仅仅是它的一个位置,后面还会再讲这个代理会在别的地方进行创建的,目前创建一个对象是在BeanPostProcessor中完成的。

9.6 复述总结

9.6.1 自定义类型转换器是RuntimeBeanReferences处理,基本类型是由TypedStringValue进行处理

在populateBean方法中applyPropertyValues是处理bean标签处理xml的

RuntimeBeanReferences处理是在这个地方
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
TypedStringValue处理是在这个地方
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);

9.6.2 、注解 @Value @Autowired

在populateBean经过判断是注解处理,就会在一个for循环中,找到AutowiredAnnotationBeanPostProcessor进行处理,会把你的属性值都给提取出来,然后遍历一一注入赋值。如果是@Value类型的话,那么就是说直接通过配置文件去解析获取值,如果是@Autiwired它还是会走getBean去获取到这个bean然后再进行赋值。

9.6.3、属性填充的总结

不管通过

9.6.4、初始化

初始化呢 Aware BeanPostProcessorBefore initBean【接口】|| initMethod【自定义方法】 BeanPostProcessorBefore

有一个问题就是说AOP的本质是什么?是BeanPostProcessor

AOP 代理对象的创建会在哪里进行呢? 创建对象?属性填充?初始化?

创建对象 创建的是原始对象

属性填充 只是处理属性

初始化设计到5个时机,最终经过测试是在BeanPostProcessorBefore 中完成代理对象的创建

为啥会在这个地方呢?

可能是为了安全,防止你在初始化的过程,做一些额外操作,为了保证处理的安全所以在最后做操作。

其实AOP的代理对象不止这一个地方,我们后面会见到的。

你可能感兴趣的:(spring,java,后端)