摘要:关于创建实例的过程,我将会分几篇文章进行分析。源码分析创建实例的入口在正式分析方法前,我们先来看看方法是在哪里被调用的。时,表明方法不存在,此时抛出异常。该变量用于表示是否提前暴露单例,用于解决循环依赖。
1. 简介
在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑。对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去创建,而是从缓存中获取。如果某个 bean 还未实例化,这个时候就无法命中缓存。此时,就要根据 bean 的配置信息去创建这个 bean 了。相较于getBean(String)方法的实现逻辑,创建 bean 的方法createBean(String, RootBeanDefinition, Object[])及其所调用的方法逻辑上更为复杂一些。关于创建 bean 实例的过程,我将会分几篇文章进行分析。本篇文章会先从大体上分析 createBean(String, RootBeanDefinition, Object[])方法的代码逻辑,至于其所调用的方法将会在随后的文章中进行分析。
好了,其他的不多说,直接进入正题吧。
2. 源码分析 2.1 创建 bean 实例的入口在正式分析createBean(String, RootBeanDefinition, Object[])方法前,我们先来看看 createBean 方法是在哪里被调用的。如下:
public T doGetBean(...) { // 省略不相关代码 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory
上面是 doGetBean 方法的代码片段,从中可以发现 createBean 方法。createBean 方法被匿名工厂类的 getObject 方法包裹,但这个匿名工厂类对象并未直接调用 getObject 方法。而是将自身作为参数传给了getSingleton(String, ObjectFactory)方法,那么我们接下来再去看看一下getSingleton(String, ObjectFactory) 方法的实现。如下:
public Object getSingleton(String beanName, ObjectFactory> singletonFactory) { Assert.notNull(beanName, ""beanName" must not be null"); synchronized (this.singletonObjects) { // 从缓存中获取单例 bean,若不为空,则直接返回,不用再初始化 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean "" + beanName + """); } /* * 将 beanName 添加到 singletonsCurrentlyInCreation 集合中, * 用于表明 beanName 对应的 bean 正在创建中 */ beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet(); } try { // 通过 getObject 方法调用 createBean 方法创建 bean 实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } // 将 beanName 从 singletonsCurrentlyInCreation 移除 afterSingletonCreation(beanName); } if (newSingleton) { /* * 将 键值对添加到 singletonObjects 集合中, * 并从其他集合(比如 earlySingletonObjects)中移除 singletonObject 记录 */ addSingleton(beanName, singletonObject); } } return (singletonObject != NULL_OBJECT ? singletonObject : null); } }
上面的方法逻辑不是很复杂,这里简单总结一下。如下:
先从 singletonObjects 集合获取 bean 实例,若不为空,则直接返回
若为空,进入创建 bean 实例阶段。先将 beanName 添加到 singletonsCurrentlyInCreation
通过 getObject 方法调用 createBean 方法创建 bean 实例
将 beanName 从 singletonsCurrentlyInCreation 集合中移除
将
从上面的分析中,我们知道了 createBean 方法在何处被调用的。那么接下来我们一起深入 createBean 方法的源码中,来看看这个方法具体都做了什么事情。
2.2 createBean 方法全貌createBean 和 getBean 方法类似,基本上都是空壳方法,只不过 createBean 的逻辑稍微多点,多做了一些事情。下面我们一起看看这个方法的实现逻辑,如下:
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean "" + beanName + """); } RootBeanDefinition mbdToUse = mbd; // 解析 bean 的类型 Class> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } try { // 处理 lookup-method 和 replace-method 配置,Spring 将这两个配置统称为 override method mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // 在 bean 初始化前应用后置处理,如果后置处理返回的 bean 不为空,则直接返回 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } // 调用 doCreateBean 创建 bean Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean "" + beanName + """); } return beanInstance; }
上面的代码不长,代码的执行流程比较容易看出,这里罗列一下:
解析 bean 类型
处理 lookup-method 和 replace-method 配置
在 bean 初始化前应用后置处理,若后置处理返回的 bean 不为空,则直接返回
若上一步后置处理返回的 bean 为空,则调用 doCreateBean 创建 bean 实例
下面我会分节对第2、3和4步的流程进行分析,步骤1的详细实现大家有兴趣的话,就自己去看看吧。
2.2.1 验证和准备 override 方法当用户配置了 lookup-method 和 replace-method 时,Spring 需要对目标 bean 进行增强。在增强之前,需要做一些准备工作,也就是 prepareMethodOverrides 中的逻辑。下面来看看这个方法的源码:
public void prepareMethodOverrides() throws BeanDefinitionValidationException { MethodOverrides methodOverrides = getMethodOverrides(); if (!methodOverrides.isEmpty()) { Setoverrides = methodOverrides.getOverrides(); synchronized (overrides) { // 循环处理每个 MethodOverride 对象 for (MethodOverride mo : overrides) { prepareMethodOverride(mo); } } } } protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException { // 获取方法名为 mo.getMethodName() 的方法数量,当方法重载时,count 的值就会大于1 int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName()); // count = 0,表明根据方法名未找到相应的方法,此时抛出异常 if (count == 0) { throw new BeanDefinitionValidationException( "Invalid method override: no method with name "" + mo.getMethodName() + "" on class [" + getBeanClassName() + "]"); } // 若 count = 1,表明仅存在已方法名为 mo.getMethodName(),这意味着方法不存在重载 else if (count == 1) { // 方法不存在重载,则将 overloaded 成员变量设为 false mo.setOverloaded(false); } }
上面的源码中,prepareMethodOverrides方法循环调用了prepareMethodOverride方法,并没其他的太多逻辑。主要准备工作都是在 prepareMethodOverride 方法中进行的,所以我们重点关注一下这个方法。prepareMethodOverride 这个方法主要用于获取指定方法的方法数量 count,并根据 count 的值进行相应的处理。count = 0 时,表明方法不存在,此时抛出异常。count = 1 时,设置 MethodOverride 对象的overloaded成员变量为 false。这样做的目的在于,提前标注名称mo.getMethodName()的方法不存在重载,在使用 CGLIB 增强阶段就不需要进行校验,直接找到某个方法进行增强即可。
上面的方法没太多的逻辑,比较简单,就先分析到这里。
2.2.2 bean 实例化前的后置处理后置处理是 Spring 的一个拓展点,用户通过实现 BeanPostProcessor 接口,并将实现类配置到 Spring 的配置文件中(或者使用注解),即可在 bean 初始化前后进行自定义操作。关于后置处理较为详细的说明,可以参考我的了一篇文章Spring IOC 容器源码分析系列文章导读,这里就不赘述了。下面我们来看看 createBean 方法中的后置处理逻辑,如下:
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; // 检测是否解析过,mbd.beforeInstantiationResolved 的值在下面的代码中会被设置 if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class> targetType = determineTargetType(beanName, mbd); if (targetType != null) { // 应用前置处理 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { // 应用后置处理 bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } // 设置 mbd.beforeInstantiationResolved mbd.beforeInstantiationResolved = (bean != null); } return bean; } protected Object applyBeanPostProcessorsBeforeInstantiation(Class> beanClass, String beanName) { for (BeanPostProcessor bp : getBeanPostProcessors()) { // InstantiationAwareBeanPostProcessor 一般在 Spring 框架内部使用,不建议用户直接使用 if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // bean 初始化前置处理 Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } } return null; } public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { // bean 初始化后置处理 result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
在 resolveBeforeInstantiation 方法中,当前置处理方法返回的 bean 不为空时,后置处理才会被执行。前置处理器是 InstantiationAwareBeanPostProcessor 类型的,该种类型的处理器一般用在 Spring 框架内部,比如 AOP 模块中的AbstractAutoProxyCreator抽象类间接实现了这个接口中的 postProcessBeforeInstantiation方法,所以 AOP 可以在这个方法中生成为目标类的代理对象。不过我在调试的过程中,发现 AOP 在此处生成代理对象是有条件的。一般情况下条件都不成立,也就不会在此处生成代理对象。至于这个条件为什么不成立,因 AOP 这一块的源码我还没来得及看,所以暂时还无法解答。等我看过 AOP 模块的源码后,我再来尝试分析这个条件。
2.2.3 调用 doCreateBean 方法创建 bean这一节,我们来分析一下doCreateBean方法的源码。在 Spring 中,做事情的方法基本上都是以do开头的,doCreateBean 也不例外。那下面我们就来看看这个方法都做了哪些事情。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { /* * BeanWrapper 是一个基础接口,由接口名可看出这个接口的实现类用于包裹 bean 实例。 * 通过 BeanWrapper 的实现类可以方便的设置/获取 bean 实例的属性 */ BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { // 从缓存中获取 BeanWrapper,并清理相关记录 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { /* * 创建 bean 实例,并将实例包裹在 BeanWrapper 实现类对象中返回。createBeanInstance * 中包含三种创建 bean 实例的方式: * 1. 通过工厂方法创建 bean 实例 * 2. 通过构造方法自动注入(autowire by constructor)的方式创建 bean 实例 * 3. 通过无参构造方法方法创建 bean 实例 * * 若 bean 的配置信息中配置了 lookup-method 和 replace-method,则会使用 CGLIB * 增强 bean 实例。关于这个方法,后面会专门写一篇文章介绍,这里先说这么多。 */ instanceWrapper = createBeanInstance(beanName, mbd, args); } // 此处的 bean 可以认为是一个原始的 bean 实例,暂未填充属性 final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); mbd.resolvedTargetType = beanType; // 这里又遇到后置处理了,此处的后置处理是用于处理已“合并的 BeanDefinition”。关于这种后置处理器具体的实现细节就不深入理解了,大家有兴趣可以自己去看 synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } /* * earlySingletonExposure 是一个重要的变量,这里要说明一下。该变量用于表示是否提前暴露 * 单例 bean,用于解决循环依赖。earlySingletonExposure 由三个条件综合而成,如下: * 条件1:mbd.isSingleton() - 表示 bean 是否是单例类型 * 条件2:allowCircularReferences - 是否允许循环依赖 * 条件3:isSingletonCurrentlyInCreation(beanName) - 当前 bean 是否处于创建的状态中 * * earlySingletonExposure = 条件1 && 条件2 && 条件3 * = 单例 && 是否允许循环依赖 && 是否存于创建状态中。 */ boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean "" + beanName + "" to allow for resolving potential circular references"); } // 添加工厂对象到 singletonFactories 缓存中 addSingletonFactory(beanName, new ObjectFactory
上面的注释比较多,分析的应该比较详细的。不过有一部分代码我暂时没看懂,就不分析了,见谅。下面我们来总结一下 doCreateBean 方法的执行流程吧,如下:
从缓存中获取 BeanWrapper 实现类对象,并清理相关记录
若未命中缓存,则创建 bean 实例,并将实例包裹在 BeanWrapper 实现类对象中返回
应用 MergedBeanDefinitionPostProcessor 后置处理器相关逻辑
根据条件决定是否提前暴露 bean 的早期引用(early reference),用于处理循环依赖问题
调用 populateBean 方法向 bean 实例中填充属性
调用 initializeBean 方法完成余下的初始化工作
注册销毁逻辑
doCreateBean 方法的流程比较复杂,步骤略多。由此也可了解到创建一个 bean 还是很复杂的,这中间要做的事情繁多。比如填充属性、对 BeanPostProcessor 拓展点提供支持等。以上的步骤对应的方法具体是怎样实现的,本篇文章并不打算展开分析。在后续的文章中,我会多带带写文章分析几个逻辑比较复杂的步骤。有兴趣的阅读的朋友可以稍微等待一下,相关文章本周会陆续进行更新。
3. 总结到这里,createBean 方法及其所调用的方法的源码就分析完了。总的来说,createBean 方法还是比较复杂的,需要多看几遍才能理清一些头绪。由于 createBean 方法比较复杂,对于以上的源码分析,我并不能保证不出错。如果有写错的地方,还请大家指点迷津。毕竟当局者迷,作为作者,我很难意识到哪里写的有问题。
好了,本篇文章到此结束。谢谢阅读。
参考《Spring 源码深度解析》- 郝佳
附录:Spring 源码分析文章列表 Ⅰ. IOC更新时间 | 标题 |
---|---|
2018-05-30 | Spring IOC 容器源码分析系列文章导读 |
2018-06-01 | Spring IOC 容器源码分析 - 获取单例 bean |
2018-06-04 | Spring IOC 容器源码分析 - 创建单例 bean 的过程 |
2018-06-06 | Spring IOC 容器源码分析 - 创建原始 bean 对象 |
2018-06-08 | Spring IOC 容器源码分析 - 循环依赖的解决办法 |
2018-06-11 | Spring IOC 容器源码分析 - 填充属性到 bean 原始对象 |
2018-06-11 | Spring IOC 容器源码分析 - 余下的初始化工作 |
更新时间 | 标题 |
---|---|
2018-06-17 | Spring AOP 源码分析系列文章导读 |
2018-06-20 | Spring AOP 源码分析 - 筛选合适的通知器 |
2018-06-20 | Spring AOP 源码分析 - 创建代理对象 |
2018-06-22 | Spring AOP 源码分析 - 拦截器链的执行过程 |
更新时间 | 标题 |
---|---|
2018-06-29 | Spring MVC 原理探秘 - 一个请求的旅行过程 |
2018-06-30 | Spring MVC 原理探秘 - 容器的创建过程 |
本文在知识共享许可协议 4.0 下发布,转载需在明显位置处注明出处
作者:coolblog.xyz
本文同步发布在我的个人博客:http://www.coolblog.xyz
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69639.html
摘要:初始化我们知道容器初始化后会对容器中非懒加载的,单例的以及非抽象的定义进行的初始化操作,所以我们分析源码的入口也就是在容器初始化的入口,分析容器初始化后在什么地方开始第一次的初始化。 前言 Spring IOC容器在初始化之后会对容器中非懒加载的,单例的以及非抽象的bean定义进行bean的初始化操作,同时会也涉及到Bean的后置处理器以及DI(依赖注入)等行为。对于Bean的初始化,...
摘要:简介为了写容器源码分析系列的文章,我特地写了一篇容器的导读文章。在做完必要的准备工作后,从本文开始,正式开始进入源码分析的阶段。从缓存中获取单例。返回以上就是和两个方法的分析。 1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章。在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一些建议。在...
摘要:简介本篇文章是容器源码分析系列文章的最后一篇文章,本篇文章所分析的对象是方法,该方法用于对已完成属性填充的做最后的初始化工作。后置处理器是拓展点之一,通过实现后置处理器接口,我们就可以插手的初始化过程。 1. 简介 本篇文章是Spring IOC 容器源码分析系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bean 做最...
摘要:本文是容器源码分析系列文章的第一篇文章,将会着重介绍的一些使用方法和特性,为后续的源码分析文章做铺垫。我们可以通过这两个别名获取到这个实例,比如下面的测试代码测试结果如下本小节,我们来了解一下这个特性。 1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本。经过十几年的迭代,现在的 Spring 框架已经非常成熟了...
摘要:实例化时,发现又依赖于。一些缓存的介绍在进行源码分析前,我们先来看一组缓存的定义。可是看完源码后,我们似乎仍然不知道这些源码是如何解决循环依赖问题的。 1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的。在本篇文章中,我会首先向大家介绍一下什么是循环依赖。然后,进入源码分析阶段。为了更好的说明 Spring 解决循环依赖的办法,我将会从获取 bean 的方法getB...
阅读 1512·2023-04-26 02:50
阅读 3482·2023-04-26 00:28
阅读 1901·2023-04-25 15:18
阅读 3109·2021-11-24 10:31
阅读 951·2019-08-30 13:00
阅读 966·2019-08-29 15:19
阅读 1730·2019-08-29 13:09
阅读 2943·2019-08-29 13:06