[learning]《Spring源码深度解析》第四、五章笔记

0.What’s this?

这是一篇关于《Spring源码深度解析》的笔记

第四章 自定义标签的解析

自定义标签的核心源于代码中调用的delegate.parseCustomElement(root)。

4.1 自定义标签的使用

自定义标签的配置过程(详细过程略):

  1. 创建一个需要扩展的组件
  2. 定义一个XSD文件描述组件的内容
  3. 创建一个类,实现BeanDefinitionParser接口,用于解析XSD文件中的定义和组件的定义
  4. 定义一个Handler类,继承NamespaceHandlerSupport,用于将组件注册到Spring容器
  5. 编写Spring.handlers和Spring.schemas文件

4.2 自定义标签解析

4.2.1 获取标签的命名空间

直接调用w3c提供的方法:getNamespaceURI即可。

4.2.2 提取自定义标签处理器

调用DefaultNamespaceHandlerResolver的resolve方法,获取NamespaceHandler:

  1. 调用getHandlerMappings,获取所有已经配置的handler映射
  2. 根据命名空间找到对应的handler信息
  3. 已经做过解析的,直接从缓存中读取,否则返回一个类路径,将其通过反射转换为类并初始化,调用该Handler的init方法,然后将其记录到缓存中

4.2.3 标签解析

标签解析时调用自定义标签处理器的parse方法,该方法内首先调用findParserForElement,找到元素对应的解析器,进而调用解析器的parse方法。

BeanDefinitionParser.parse方法中,处理标签解析的核心方法是parseInternal方法:

  1. 方法使用BeanDefinitionBuilder来构建一个BeanDefinition
  2. 首先调用解析器中的getbeanClass方法获取自定义标签中的class,若该方法未重写,则会调用getBeanCalssName方法获取类名
  3. 若存在父类则会set父类的scope属性
  4. 调用子类重写的doParse方法进行解析(需要自定义实现)

通过以上步骤获取到一个AbstractBeanDefinition后,BeanDefinitionParser.parse方法继续将其转换为BeanDefinitionHolder并注册,在通知监听器进行相应的处理。

第五章 Bean的加载

通过调用BeanFactory的getBean方法,传入Bean Name获取相应的bean,该方法调用了私有方法doGetBean进行加载,加载流程:

  1. 获取Bean Name,入参的name可能包含FactoryBean的修饰符,因此需要去掉修饰符,接下来需要避免name是alias的情况,需要映射到底,取到最终的bean name。
  2. 尝试从缓存中加载单例,这里有三个缓存map,首先尝试从singletonObjects中尝试获取,这个map中装载的是真正创建好的单例bean对象,如果没有,则寻找earlySingletonObjects中是否有,该map中的对象是早期创建但未初始化的bean,如果仍然没有,则搜索第三个map singletonFactories,该map里缓存了bean的实例工厂,找到对应工厂,实例化bean并装入earlySingletonObjects中,然后将实例工厂移除。
  3. 缓存中拿到的bean可能是factory bean,即bean的原始状态,如果不是factory bean,方法到此返回拿到的bean,否则,调用getObjectFromFactoryBean方法获取我们想要的bean,先从缓存中取,若没有则调用doGetObjectFromFactoryBean方法从factoryBean中获取bean。(factoryBean.getObject())
  4. 若缓存中未拿到bean,则判断该bean的scope是否是prototype(原型),并判断这个bean name是否正在被其他线程创建,如果是则抛出异常,否则转向父类bean的工厂,如果beanDefinitionMap中没有找到这个bean对应的beanDefinition,需要将之前移除的FactorBean前缀重新添加回来得到原始bean name,再尝试从parentBeanFactory中获取bean。
  5. 接下来会将GernericBeanDefinition转换为RootBeanDefinition,前者存储了从XML配置文件中读取到的Bean信息,而对bean的后续处理都是针对后者的,因此需要进行转换,如果父类bean不为空,则此处还会合并父类的属性。
  6. 寻找当前加载的bean的依赖,按顺序加载所有依赖的bean。首先遍历dependentBeanMap,寻找当前的bean依赖哪些bean,从中检查是否有循环依赖,随后注册依赖关系,然后递归创建所有以来集合中的bean。
  7. 针对不同的scope进行bean的创建。
  8. 如果需要的话,将bean转换为requiredType并返回。

5.1 FactoryBean

一般情况下,Spring是通过反射机制来实例化bean的,但是某些情况下,如果Bean有大量的字段,实例化bean的过程比较复杂,需要在标签中提供大量配置信息,使得灵活性受限。使用FactoryBean可以解决这个问题。

FactoryBean接口是Spring重要的接口,该接口定义了三个方法:

  1. getObject() -> 返回FactoryBean创建的bean实例,如果方法2返回true,则该实例还会放入Spring的单例缓存池中
  2. isSingleton() -> 如果bean的作用域是单例,则返回true
  3. getObjectType() -> 返回FactoryBean创建的bean的类型

例如:约定使用String字符串存储某个对象的全部字段,并用某个字符分隔,则getObject方法中,可以通过该分隔符提取出各个字段,进行赋值
则在配置文件中,可以指定该bean的属性为单个String类型。

5.2 从缓存中获取单例bean

单例的bean只会创建一次,后续需要这个bean时会从单例缓存中获取,如果尝试从缓存中加载失败,则在尝试从singletonFactories中加载。因为创建单例bean时存在依赖注入的情况,这里需要检查循环依赖,存在循环依赖时抛出异常。

5.3 从bean的实例中获取对象

无论是从缓存中获取bean,或者根据不同的scope加载bean,得到的都可能是FactoryBean而不是我们最终想要的bean,在得到bean实例时,需要调用getObjectForBeanInstance方法来对bean进行检查,如果是FactoryBean,则需要通过FactoryBean的getObject方法获取到bean。工作过程:

  1. 验证FactoryBean的正确性,直接返回非FactoryBean
  2. 将GenericBeanDefinition转换为RootBeanDefinition,如果指定的BeanName是子类Bean,则会合入父类的相关属性
  3. 调用getObjectFromFactoryBean,从Factory中解析出Bean

方法getObjectFromFactoryBean中核心的部分即调用factroy的getObject方法获取bean,对于需要权限验证的会做额外处理来过去权限。获取到bean对象后,根据该方法的入参来确认是否需要后处理,需要则调用ObjectFactory的后处理器对bean对象进行处理。

5.4 获取单例

如果单例缓存中存在bean,则直接从缓存中获取,如果没有,则通过getSingleton的重载方法来实现bean的加载过程:

  1. 全局变量加锁
  2. 检查bean是否已经加载过,如果加载过则直接返回bean
  3. 检查bean当前的状态是否为InDestruction,目的是禁止在bean的销毁方法中请求获取这个bean
  4. 调用beforeSingletonCreation,记录当前bean的加载状态,用于对循环依赖进行检测
  5. 通过调用参数传入的ObjectFactory的getObject方法实例化bean
  6. 调用afterSingletonCreation方法,移除步骤4中记录的加载状态
  7. 调用addSingleton方法,将结果记录至缓存中,并删除加载bean过程中所记录的各种辅助状态,如singletonFactories中记录的BeanFactory,earlySingletonObjects中记录的未实例化的bean等。
  8. 返回处理结果

5.5 准备创建bean

核心方法以do开头,以get开头的方法通常是做前置和后续处理工作的。

bean的创建是在方法中,调用入参ObjectFactory的getObject方法来完成的,getObject调用了接口实现的createBean:

  1. 锁定class,根据class属性或className来解析class
  2. 对override属性进行标记和验证,针对于lookup-method和replace-method配置进行。
  3. 调用resolveBeforeInstantiation方法,让BeanPostProcessors检查存在初始化前的短路操作,此处可能返回一个代理来替换bean实例
  4. 调用doCreateBean创建bean

方法resolveBeforeInstantiation返回的结果如果不为空,则会忽略掉后续的创建bean的逻辑,而直接返回resolveBeforeInstantiation的结果。该方法会调用applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization方法。

applyBeanPostProcessorsBeforeInitialization
程序经过若干该方法后,bean可能是一个经过处理的代理bean,因此需要将AbstractBeanDefinition转换为BeanWrapper,给子类提供修改bean的机会。

applyBeanPostProcessorsAfterInitialization
如果本次创建获取的结果bean不为空,则不再会经历普通bean的创建过程,因此在这里将beanProcessors的后处理器方法应用到所有的bean上

如果以上方法内没有创建代理,并且重写的后处理器方法没有改变bean,则需要进行常规的bean创建,过程在方法doCreateBean中完成:

  1. 根据bean使用的策略创建新的实例,如工厂方法、构造函数注入、简单实例化等
  2. 检查是否需要提前暴露(单例 & 允许循环依赖 & 当前bean在创建中),如果需要提前暴露,则将创建实例的ObjectFactory放入工厂map中,以避免后续出现的循环依赖
  3. 调用populateBean方法,对bean进行填充,注入各项属性值,如果此处依赖其他bean的属性,则会哦递归初始化被依赖的bean
  4. 调用initializeBean方法,对bean进行初始化
  5. 如果需要提前暴露,则在此检查是否有循环依赖:如果bean存在earlySingletonReference,且提前暴露的bean在初始化方法中没有被改变,则会对当前bean依赖的所有bean进行检查,至此,当前创建的bean的所有依赖的bean应该都已经创建完成,遍历当前bean依赖的所有bean,并逐一检查它们的依赖,如果找到了没有创建完成的bean则说明存在循环依赖
  6. 根据scope注册新创建的bean,并返回创建的bean。

5.6 循环依赖

Spring容器循环依赖包括构造器循环依赖和setter循环依赖。Spring将循环依赖的处理分成了三种情况:

5,6.1 构造器循环依赖

表示用过构造器注入构成的循环依赖,类A在创建时需要创建B,创建B时发现需要依赖A,则构成循环依赖该依赖无法解决,只能抛出异常表示循环依赖。

5.6.2 setter循环依赖

setter注入依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤的bean造成的。通过无参构造器创建一个A bean,然后通过setter注入B bean,随后创建B bean,通过setter注入A bean,此时产生循环依赖。这里的解决方法是在创建A bean之后,未开始setter注入前,将A的标识符放到“当前创建的bean池“中,暴露一个ObjectFactory,用于返回一个创建中的bean,使得B在进行setter注入时能够拿到A的引用(或者别的什么东西)。该方案只能解决单例作用域bean的循环依赖。

5.6.3 prototype范围的依赖

setter循环依赖之所以能够通过”提前暴露“来解决,是因为Spring在创建单例bean的过程中会进行缓存,但是prototype作用域的bean不会进行缓存,因此无法提前暴露一个正在创建的bean。
如果是单例的bean,可以通过setAllowCircularReferences(false)来禁止循环依赖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值