0.What’s this?
这是一篇关于《Spring源码深度解析》的笔记
第四章 自定义标签的解析
自定义标签的核心源于代码中调用的delegate.parseCustomElement(root)。
4.1 自定义标签的使用
自定义标签的配置过程(详细过程略):
- 创建一个需要扩展的组件
- 定义一个XSD文件描述组件的内容
- 创建一个类,实现BeanDefinitionParser接口,用于解析XSD文件中的定义和组件的定义
- 定义一个Handler类,继承NamespaceHandlerSupport,用于将组件注册到Spring容器
- 编写Spring.handlers和Spring.schemas文件
4.2 自定义标签解析
4.2.1 获取标签的命名空间
直接调用w3c提供的方法:getNamespaceURI即可。
4.2.2 提取自定义标签处理器
调用DefaultNamespaceHandlerResolver的resolve方法,获取NamespaceHandler:
- 调用getHandlerMappings,获取所有已经配置的handler映射
- 根据命名空间找到对应的handler信息
- 已经做过解析的,直接从缓存中读取,否则返回一个类路径,将其通过反射转换为类并初始化,调用该Handler的init方法,然后将其记录到缓存中
4.2.3 标签解析
标签解析时调用自定义标签处理器的parse方法,该方法内首先调用findParserForElement,找到元素对应的解析器,进而调用解析器的parse方法。
BeanDefinitionParser.parse方法中,处理标签解析的核心方法是parseInternal方法:
- 方法使用BeanDefinitionBuilder来构建一个BeanDefinition
- 首先调用解析器中的getbeanClass方法获取自定义标签中的class,若该方法未重写,则会调用getBeanCalssName方法获取类名
- 若存在父类则会set父类的scope属性
- 调用子类重写的doParse方法进行解析(需要自定义实现)
通过以上步骤获取到一个AbstractBeanDefinition后,BeanDefinitionParser.parse方法继续将其转换为BeanDefinitionHolder并注册,在通知监听器进行相应的处理。
第五章 Bean的加载
通过调用BeanFactory的getBean方法,传入Bean Name获取相应的bean,该方法调用了私有方法doGetBean进行加载,加载流程:
- 获取Bean Name,入参的name可能包含FactoryBean的修饰符,因此需要去掉修饰符,接下来需要避免name是alias的情况,需要映射到底,取到最终的bean name。
- 尝试从缓存中加载单例,这里有三个缓存map,首先尝试从singletonObjects中尝试获取,这个map中装载的是真正创建好的单例bean对象,如果没有,则寻找earlySingletonObjects中是否有,该map中的对象是早期创建但未初始化的bean,如果仍然没有,则搜索第三个map singletonFactories,该map里缓存了bean的实例工厂,找到对应工厂,实例化bean并装入earlySingletonObjects中,然后将实例工厂移除。
- 缓存中拿到的bean可能是factory bean,即bean的原始状态,如果不是factory bean,方法到此返回拿到的bean,否则,调用getObjectFromFactoryBean方法获取我们想要的bean,先从缓存中取,若没有则调用doGetObjectFromFactoryBean方法从factoryBean中获取bean。(factoryBean.getObject())
- 若缓存中未拿到bean,则判断该bean的scope是否是prototype(原型),并判断这个bean name是否正在被其他线程创建,如果是则抛出异常,否则转向父类bean的工厂,如果beanDefinitionMap中没有找到这个bean对应的beanDefinition,需要将之前移除的FactorBean前缀重新添加回来得到原始bean name,再尝试从parentBeanFactory中获取bean。
- 接下来会将GernericBeanDefinition转换为RootBeanDefinition,前者存储了从XML配置文件中读取到的Bean信息,而对bean的后续处理都是针对后者的,因此需要进行转换,如果父类bean不为空,则此处还会合并父类的属性。
- 寻找当前加载的bean的依赖,按顺序加载所有依赖的bean。首先遍历dependentBeanMap,寻找当前的bean依赖哪些bean,从中检查是否有循环依赖,随后注册依赖关系,然后递归创建所有以来集合中的bean。
- 针对不同的scope进行bean的创建。
- 如果需要的话,将bean转换为requiredType并返回。
5.1 FactoryBean
一般情况下,Spring是通过反射机制来实例化bean的,但是某些情况下,如果Bean有大量的字段,实例化bean的过程比较复杂,需要在标签中提供大量配置信息,使得灵活性受限。使用FactoryBean可以解决这个问题。
FactoryBean接口是Spring重要的接口,该接口定义了三个方法:
- getObject() -> 返回FactoryBean创建的bean实例,如果方法2返回true,则该实例还会放入Spring的单例缓存池中
- isSingleton() -> 如果bean的作用域是单例,则返回true
- 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。工作过程:
- 验证FactoryBean的正确性,直接返回非FactoryBean
- 将GenericBeanDefinition转换为RootBeanDefinition,如果指定的BeanName是子类Bean,则会合入父类的相关属性
- 调用getObjectFromFactoryBean,从Factory中解析出Bean
方法getObjectFromFactoryBean中核心的部分即调用factroy的getObject方法获取bean,对于需要权限验证的会做额外处理来过去权限。获取到bean对象后,根据该方法的入参来确认是否需要后处理,需要则调用ObjectFactory的后处理器对bean对象进行处理。
5.4 获取单例
如果单例缓存中存在bean,则直接从缓存中获取,如果没有,则通过getSingleton的重载方法来实现bean的加载过程:
- 全局变量加锁
- 检查bean是否已经加载过,如果加载过则直接返回bean
- 检查bean当前的状态是否为InDestruction,目的是禁止在bean的销毁方法中请求获取这个bean
- 调用beforeSingletonCreation,记录当前bean的加载状态,用于对循环依赖进行检测
- 通过调用参数传入的ObjectFactory的getObject方法实例化bean
- 调用afterSingletonCreation方法,移除步骤4中记录的加载状态
- 调用addSingleton方法,将结果记录至缓存中,并删除加载bean过程中所记录的各种辅助状态,如singletonFactories中记录的BeanFactory,earlySingletonObjects中记录的未实例化的bean等。
- 返回处理结果
5.5 准备创建bean
核心方法以do开头,以get开头的方法通常是做前置和后续处理工作的。
bean的创建是在方法中,调用入参ObjectFactory的getObject方法来完成的,getObject调用了接口实现的createBean:
- 锁定class,根据class属性或className来解析class
- 对override属性进行标记和验证,针对于lookup-method和replace-method配置进行。
- 调用resolveBeforeInstantiation方法,让BeanPostProcessors检查存在初始化前的短路操作,此处可能返回一个代理来替换bean实例
- 调用doCreateBean创建bean
方法resolveBeforeInstantiation返回的结果如果不为空,则会忽略掉后续的创建bean的逻辑,而直接返回resolveBeforeInstantiation的结果。该方法会调用applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization方法。
applyBeanPostProcessorsBeforeInitialization
程序经过若干该方法后,bean可能是一个经过处理的代理bean,因此需要将AbstractBeanDefinition转换为BeanWrapper,给子类提供修改bean的机会。applyBeanPostProcessorsAfterInitialization
如果本次创建获取的结果bean不为空,则不再会经历普通bean的创建过程,因此在这里将beanProcessors的后处理器方法应用到所有的bean上
如果以上方法内没有创建代理,并且重写的后处理器方法没有改变bean,则需要进行常规的bean创建,过程在方法doCreateBean中完成:
- 根据bean使用的策略创建新的实例,如工厂方法、构造函数注入、简单实例化等
- 检查是否需要提前暴露(单例 & 允许循环依赖 & 当前bean在创建中),如果需要提前暴露,则将创建实例的ObjectFactory放入工厂map中,以避免后续出现的循环依赖
- 调用populateBean方法,对bean进行填充,注入各项属性值,如果此处依赖其他bean的属性,则会哦递归初始化被依赖的bean
- 调用initializeBean方法,对bean进行初始化
- 如果需要提前暴露,则在此检查是否有循环依赖:如果bean存在earlySingletonReference,且提前暴露的bean在初始化方法中没有被改变,则会对当前bean依赖的所有bean进行检查,至此,当前创建的bean的所有依赖的bean应该都已经创建完成,遍历当前bean依赖的所有bean,并逐一检查它们的依赖,如果找到了没有创建完成的bean则说明存在循环依赖
- 根据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)来禁止循环依赖。