用vs2010做网站视频教程,网站推广的特点,网站建设亿玛酷信赖,预约网站如何自己做一、Spring Bean创建过程以及循环依赖
上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析#xff0c;我们可以得到结论#xff0c;资源文件中的 bean 定义信息#xff0c;被组装成了 BeanDefinition 存放进了 beanDefinitionMap 容器中#xff0c;那 Bean 是…一、Spring Bean创建过程以及循环依赖
上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析我们可以得到结论资源文件中的 bean 定义信息被组装成了 BeanDefinition 存放进了 beanDefinitionMap 容器中那 Bean 是怎样创建和依赖注入的还没有进行分析而且这里还有个经典的循环依赖问题本篇文章将带领大家一起继续上篇文章进行Spring源码的分析梳理下Bean创建过程以及循环依赖问题的解决。
下面是上篇文章的地址 Spring 源码解析 - Bean资源加载注册过程 在上篇文章我们主要分析的 AbstractApplicationContext 下 refresh() 方法中的 obtainFreshBeanFactory() 方法的作用通过该方法我们了解到如何创建的 BeanFactory 工厂以及如何生成 BeanDefinition 并注册到容器本篇文章我们还是从 refresh() 方法中找突破点从 finishBeanFactoryInitialization(beanFactory) 入手分析该方法主要做了对所有的单例类型 bean 进行初始化以及依赖注入 二、finishBeanFactoryInitialization(beanFactory) 点击到该方法中直接看到方法最后的 beanFactory.preInstantiateSingletons() 方法中 在 preInstantiateSingletons() 方法中可以明显看到拿到了 beanDefinitionNames 列表而这个列表则是存储所有注册的 beanDefinition 定义信息。
下面接着判断是否为单例模式并且不是懒加载这种情况下在项目启动时才需要进行实例化。 下面会判断当前的 beanName 是否为 FactoryBean 如果不是的话则使用 getBean(beanName) 从这命名上我们也可以猜出 getBean(beanName) 肯定是做了对 bean 的创建下面主要分析该方法其实该方法调用的是AbstractBeanFactory 下的 getBean(beanName) 。 这里又调用了当前类的 doGetBean 方法到这之后我们先不着急继续向下分析。
当我们在使用 Spring 时需要使用 bean 时则一般都是通过 ApplicationContext 的 getBean(String name) 方法获取一个指定的对象使用方式如下图所示 那这个 getBean(String name) 方法和上面我们分析得到的 getBean(beanName) 是否为一个呢下面我们可以先看下 ApplicationContext 类的 getBean(String name) 方法最终使用的哪个方法来获取 bean。
三、ApplicationContext 下的 getBean(String name) 方法
在 ApplicationContext 中并没有重写 getBean(String name) 方法实际的 getBean(String name) 其实是调用的 AbstractApplicationContext下的 getBean(String name) 在该方法中又使用了当前的 BeanFactory 中的 getBean(String name, ClassT requiredType) 方法那此时的 BeanFactory 是具体哪个子类呢
在上篇文章中我们得出结论中其中 BeanFactory 默认是使用 DefaultListableBeanFactory而 DefaultListableBeanFactory 并没有对 getBean(String name) 方法进行重写那继续去父类 AbstractAutowireCapableBeanFactory 中找也没有对 getBean(String name) 方法重写继续向父类 AbstractBeanFactory 找发现有 getBean(String name) 的重写。 看到这里是不是很有印象和前面初始化单例分析时调用了相同的方法进行初始化 bean 那此时我们就可以得出结论不管是单例还是原型或者其他最终都是使用的 AbstractBeanFactory 中的 getBean(String name) 方法初始并获取 bean 。
下面我们重点分析下其中的 doGetBean 方法。
四、doGetBean 以及 循环依赖 在 doGetBean 方法中首先对传入的 name 进行转换因为 name 可能是别名需要转为 beanName 。
接着使用 getSingleton(String beanName) 方法获取该 beanName 的实例如果实例不为空的话则使用该 bean 。
这里的 getSingleton(String beanName) 其实是在 DefaultSingletonBeanRegistry 类下主要是从单例缓存中获取已经创建的对象这里就使用到了经典的三级缓存。 在该方法中又调用了当前类的 getSingleton(String beanName, boolean allowEarlyReference) 方法继续进到方法下 从该方法中可以明显看出首先去 singletonObjects 容器中去获取对象如果不存在的话则再去 earlySingletonObjects 容器中获取对象如果还不存在的话则从 singletonFactories 容器中获取一个 ObjectFactory 如果存在则可以通过 ObjectFactory 创建对象最后将对象存放至 earlySingletonObjects 容器中然后从singletonFactories 容器中移除。
这里如果不了解循环依赖的话是不是看着一脸懵。
什么是循环依赖呢如下图所示的这种都属于循环依赖 由于 Spring 在实例化对象时会先去尝试创建其依赖的对象这样就会出现 A 在创建中发现依赖于 B此时再去创建 B而 B 又依赖于 A 再去尝试创建 A 这种情况下不就陷入了死循环中因此为了解决这种场景增加了多级缓存的概念。
多级缓存是哪几级呢其实就是上面的 singletonObjects、earlySingletonObjects、singletonFactories 三个容器分别对应着缓存的一二三级这三个容器在 DefaultSingletonBeanRegistry 类中 三级缓存具体是怎么使用的呢
一级缓存singletonObjects存储所有的单例bean 二级缓存earlySingletonObjects存储提前暴露的bean里面的bean都是没创建完的 三级缓存singletonFactories存储单例 bean的ObjectFactory也就是创建该bean的工厂
创建对象时首先会将创建 bean 的 ObjectFactory 暴露至三级缓存中后面如果有依赖于该 bean 的则会先从一二三级缓存中挨个尝试取数据直接拿到如果是在三级缓存中存在则通过 ObjectFactory获取一个早期的 bean 对象并且放入二级缓存中三级缓存中的移除后面使用该早期的bean对象进行依赖注入。再后面当该早期 bean 的依赖也被注入完成后此时就是一个完整的对象了需要从二级缓存中移除放入一级缓存中供上层使用。
了解这些之后就不难理解 getSingleton(String beanName, boolean allowEarlyReference) 方法中的逻辑了下面在回到 AbstractBeanFactory 类的 doGetBean 方法下 如果从 getSingleton(String beanName) 中获取的实例为空则缓存中不存在那就要看如果进行实例化的了。
这里首先会判断是否原型模式下形成了循环依赖如果是则抛出异常因此这里可以得出 Spring 原型模式下不允许出现循环依赖。
这里可以看下 isPrototypeCurrentlyInCreation 方法。 可以看到这里的prototypesCurrentlyInCreation其实就是一个 ThreadLocal 判断的依据就是当前 ThreadLoad 等于或者包含该 beanName 即认为是出现了循环依赖那该 ThreadLocal 什么时候写数据的呢再回到 doGetBean 方法中继续向下分析。 接着会获取到上级的 BeanFactory 并且如果当前的 beanName 不在 beanDefinitionMap 容器中则尝试用上级的 BeanFactory 进行获取 bean 一般对于我们声明好的 bean 肯定都会存在于 beanDefinitionMap 容器中。 再接着会根据 beanDefinitionMap 容器中的 BeanDefinition 创建一个 RootBeanDefinition 接着会遍历全部的依赖首先判断是否已经注册依赖了主要判断 dependentBeanMap 容器中是否存在如果不存在的话则进行注册将该依赖加入到dependentBeanMap 中最后递归的方式调用 getBean(String name) 创建依赖的对象。 在接着就要进行当前 bean 的创建工作了首先如果是单例模式下则使用 getSingleton(String beanName, ObjectFactory? singletonFactory) 方法获取实例对象并传入一个 ObjectFactory 函数其中 getSingleton 方法其实在 DefaultSingletonBeanRegistry下进入到该方法中 在该方法中首先对一级缓存进行上锁然后再次尝试从一级缓存中获取对象如果还是不存在的话并且单例对象正在销毁的话则抛出异常否则的话则执行创建 bean 的前方法主要写入 singletonsCurrentlyInCreation 容器中表示当前 bean 正在创建中。这个容器名称可以记一下会在后面进行判断是否是创建中的 bean 。 下面接着使用 newSingleton 作为创建对象的标记可以看到当调用 singletonFactory.getObject() 成功后则置为成功的标志而 singletonFactory 则就是前面传递进来的 ObjectFactory这里的 getObject() 其实就是执行的 AbstractAutowireCapableBeanFactory 下的 createBean 方法该方法可以放在后面分析因为原型模式也是使用的 createBean 方法创建实例对象后面可以一起分析现在我们知道它是创建实例对象的就可以。 在接着该方法的最后如果 newSingleton标记创建成功则此时对象属于一个完整的对象其中依赖注入已经在 createBean 方法中实现过了这个时候需要写入到一级缓存中曝光出来。 在写入一级缓存时首先对一级缓存加锁然后写入一级缓存并从二三级缓存移除同时记录到已注册的单例列表中。
到此 getSingleton(String beanName, ObjectFactory? singletonFactory) 方法就走完了在回到AbstractBeanFactory 的 doGetBean 继续向下看。 上面如果不是单例模式再继续判断否为原型模式如果是原型的则先调用 beforePrototypeCreation(String beanName) 将 beanName 加入到 prototypesCurrentlyInCreation ThreadLocal 中进入到该类中逻辑如下 这里 prototypesCurrentlyInCreation ThreadLocal 正对应上前面原型模式下判断循环依赖下面在回到上一步方法中可以看到和单例模式一样使用了 AbstractAutowireCapableBeanFactory 下的 createBean 方法创建实例同样该方法还是放在后面分析继续向下看 afterPrototypeCreation(String beanName) 方法此时当前 bean 已经实例化好了依赖注入同样在createBean 方法中已经完成了下面就可以将 ThreadLoad 中记录的 beanName 移除了逻辑如下 在回到上一步方法中继续向下看 接着在 else 中则表示既不是单例模式也不是原型模式下这里就不做过多的介绍了但明显的可以看到也是使用 createBean 方法创建实例对象的。 看到最后的话三种情况下创建的 bean 不为空的话则直接返回给上层也就是给到业务层可以正常使用该 bean 了。
五、createBean
从上面的分析中发现实际创建对象都使用的 AbstractAutowireCapableBeanFactory 下的 createBean 方法现在我们对该方法分析下是如何操作的进入到该方法中 这里首先拿到前面创建的 RootBeanDefinition下面根据 RootBeanDefinition 获取到当前 bean 的 Class主要判断需要创建的bean 是否可以被实例化是否可以通过当前的类加载器加载。 如果 resolvedClass 不为空并且 mbd 的 beanClass 不是 resolvedClass 话则创建一个新的 RootBeanDefinition。 接着会尝试创建代理对象如果是 AOP 的话则尝试使用 BeanPostProcessors 来替代真正的实例对于 AOP 的分析这里不做过多的介绍了这里主要关注下最后的 doCreateBean 方法进入到该方法中 在 doCreateBean 方法中如果是单例模式的话会尝试从 factoryBeanInstanceCache 缓存中获取一个 BeanWrapper 压缩对象如果不存在的话则通过 createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) 方法创建一个新的 BeanWrapper 对象。
这里的BeanWrapper 对象是对bean的包装可以使用统一的方式来访问bean的属性从下面的操作可以看出通过BeanWrapper 对象获取到了当前 bean 的实例对象因此在 createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) 方法中肯定对当前 bean 进行了实例化操作下面进入到该方法中 在 createBeanInstance 方法中首先通过 BeanDefinition 获得 bean 的 Class 下面如果 RootBeanDefinition 的工厂方法不为空则使用工厂方法进行初始化策略。 如果 RootBeanDefinition 的工厂方法为空接着向下看其中 resolved 表示是否已经确定好构造方法autowireNecessary 表示构造方式是否需要自动注入如果传递过来的参数为空的话那这个时候就对RootBeanDefinition 的 constructorArgumentLock 进行上锁接着会判断 RootBeanDefiniton 的 resolverConstructorOrFactoryMethod 是否为空这里其实是一个缓存后面在实例对象时会对其进行赋值这里如果存在缓存则直接使用。 下面如果构造方法已经确定的话那就是 mbd.resolvedConstructorOrFactoryMethod 缓存存在则根据 mbd 中的构造类型选择用有参的构造函数创建 bean还是使用无参的构造函数创建 bean。
这里我们假设缓存为空的话继续向下执行。 如果没有缓存的话则就要找beanName 对应的 beanClass 的所有的有参构造器如果找到了则尝试进行创建 bean 否则的话直接使用无参的构造函数进行初始化这里由于篇幅我们直接分析下 instantiateBean 无参构造函数的实例化。
进入 instantiateBean 方法中。 在该方法中没如果是否开启了安全管理如果是则使用 AccessController.doPrivileged 生成 bean 实例可以看到最终都是由 getInstantiationStrategy().instantiate 方法生成实例该方法其实在 SimpleInstantiationStrategy 类中下面进入到该方法中 在该方法中首先如果有覆盖方法的话同样对 RootBeanDefinition 的 constructorArgumentLock 进行上锁接着也还是获取 RootBeanDefiniton 的 resolverConstructorOrFactoryMethod 是否为空就是判断是否存在缓存如果不存在的话则获取到 bean 的 Class 如果 Class 是接口类型的话则抛出异常。 这一步的处理比较明了如果是安全模式下则使用 AccessController.doPrivileged 同时这里使用 clazz.getDeclaredConstructor 反射获取 class 的构造方法并且将反射得到的结果存入到resolvedConstructorOrFactoryMethod 中进行缓存对应了前面取缓存的动作最后直接通过 BeanUtils 工具类反射实例化出 bean 对象。
下面如果没有覆盖方法的话则使用 cglib 进行实例化对象 看到这里之后我们应该了解到了 bean 在哪里被创建的了下面在回到原来的 AbstractAutowireCapableBeanFactory 下的 doCreateBean 方法中其中 createBeanInstance 已经创建出了 bean 实例下面继续向下看 这里判断是否为单例模式并且允许循环依赖并且该 beanName 正在创建中的话判断是否正在创建中就是去 singletonsCurrentlyInCreation 容器中判断有无这个容器在前面有提到。
如果条件都符合的话则首先曝光至三级缓存中可以看下 addSingletonFactory 方法的过程 这里首先对一级缓存进行上锁并且一级缓存中不存在则写入到三级缓存中并从二级缓存中移除最后加入到注册单例列表中。
下面回到上一步的方法中。 接着会有一个核心的方法 populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) 方法主要进行了依赖的注入依赖注入完后接着调用 bean 的 init-method 方法进行自定义的初始化操作这里我们主要看下 populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) 方法。 在 populateBean 方法中先校验是否需要进行依赖注入接着向下看可以看到两个非常显眼的标识 这里进行了两种类型的解析 byName 和 byType 就是配置的 autowire 属性 选择依赖注入的方式是根据名称还是根据类型这里我们主要看下 autowireByName 的逻辑 这里首先获取到所有依赖的 beanName 然后如果存在容器中的话则递归的方式获取到该 bean 下面记录到上一层的 newPvs 中。
接着再回到上一层的方法中接着向下看此时依赖的属性已经都被存放在了 pvs 中可以看到最后是使用 applyPropertyValues 方法进行属性的赋值下面来看下该方法的逻辑 在 applyPropertyValues 方法中同样判断了是否开启安全管理继续向下看 这里首先判断了下属性是否被转化如果转化了就直接设置属性值否则的话就记录下在后面的逻辑中对其进行转化后再进行设置属性值。
这个转换其实就是依赖的类型有可能多种多样比如可能是引用类型、集合类型、字符串、Properties属性等等对于不同的依赖需要由不同的操作方式因此在下面的逻辑中生成了一个 BeanDefinitionValueResolver 这个是 bean 定义属性值解析器主要就是将 bean 定义中的属性值解析为 bean 实例对象的实际值 这里上面没有被转化的属性这里会进行转化下面主要看是如何进行转化的进入到 resolveValueIfNecessary 方法中 可以明显看出针对不同的类型进行了不同的解析方式这里以引用类型为例看下resolveReference(Object argName, RuntimeBeanReference ref) 方法是如何处理的 这里的逻辑也非常直观首先获取到 beanName 如果不是引用父类容器中的 bean 的话则使用 getBean 递归获取依赖的对象最后对其注册并返回给上层。
下面再回到 applyPropertyValues 方法中就好理解了最后将转化后的值通过 PropertyValue 中的 setConvertedValue 方法注入到 bean 的属性字段中。 到这里就大概看完 bean 的依赖注入下面我们再回到 AbstractAutowireCapableBeanFactory 中的 doCreateBean 方法中继续向下走的话 这里又从缓存中获取一遍 bean 但这里的 allowEarlyReference 属性为 false 也就是最多只会走一二级缓存如果取不到的话直接将前面的实例对象返回否则如果是被代理的话再次检查一遍依赖是否都加载完。
最后将处理的 bean 返回出去。
看到这里应该会对Bean的创建过程以及循环依赖有了一定的理解了吧。