Spring源码学习(二)哎呦,按菜谱做菜与AbstractAutowireCapableBeanFactory.createBean流程差不多

记得跟老婆谈恋爱时,有一天心血来潮给老婆做饭,按照菜谱一步一步的做,结果差点把厨房烧了!!! 这事至今老婆还记得。

入口

上一篇说了,AbstractBeanFactory.getBean的主流程 ,今天来说下其中的createBean方法,程序入口如下:

1/**. 2 * 这个类的核心方法,创建一个bean实例, 填充bean实例,执行后处理等 3 * @see #doCreateBean 详见doCreateBean 4 */ 5@Override 6protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) 7 throws BeanCreationException { 8 //..... 9}

根据注释核心逻辑在doCreateBean 中,下面咱们就聊聊doCreateBean.

doCreateBean 逻辑说明

主流程

类比按菜谱做菜

1. 实例化Bean BeanDefinition->BeanWrapper(如果是单例,先尝试从缓存中清楚并获取BeanWrapper)

找到菜谱,先尝试从收藏中获取

2.处理MergedBeanDefinitionPostProcessors

融合老婆的要求,到菜谱中。例如:少放盐

3.允许提前暴露ObjectFactory,解决循环引用问题(必须满足:单例&&允许循环引用&&对应的bean在创建)

提前告诉老婆菜的大概味道,方便美丽的她准备饮料,也可以方便自己提前找出盘子

4.填充属性

炒菜

5.执行 init-method方法

试吃

6.有其他bean依赖当前完整bean(必须填充完属性),移除这些bean,有不能移除的抛出异常

发现之前准备的盘子太小了,换个新的。

7.注册DisposableBean接口或destroy—method

做个好男人,吃完饭记得刷碗

源码注释在最后!!

一图胜千言(时序图)

doCreate流程

补充说明

1.实例化Bean BeanDefinition->BeanWrapper流程

BeanDerfinition已经上篇已经做过介绍(上一篇blog的传送门),下面说下BeanWrapper。 org.springframework.beans,BeanWrapper的注释翻译如下:

BeanWrapper是Spring中底层javaBean的核心接口 通常不直接使用,而是被BeanFactory和DataBinder使用 它提供对标准javabean的分析和操作的功能。 可以获取,设置属性值,获取属性值的descriptors,用于查询可读,可写的属性。 支持无限层嵌套属性。 BeanWrapper默认不支持对属性的旧值进行编辑,这样可以避免getter被调用时产生的副作用 设置 extractOldValueForEditor 为true,可以开启对旧属性值进行编辑。

知道了BeanWrapper就不难猜出BeanDefinition->BeanWrapper的大致逻辑了

BeanDefinition->BeanWrapper 就是将bean的元数据,转换为Bean,但是Bean没有对属性进行赋值。
-- 温安适 胡乱总结于20190921

createBeanInstance的源码:

1protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { 2 // 确认 bean的class,在这时已被解析了 3 Class<?> beanClass = resolveBeanClass(mbd, beanName); 4 if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { 5 throw new BeanCreationException(mbd.getResourceDescription(), beanName, 6 "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); 7 } 8 Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); 9 if (instanceSupplier != null) { 10 return obtainFromSupplier(instanceSupplier, beanName); 11 } 12 13 if (mbd.getFactoryMethodName() != null) { 14 return instantiateUsingFactoryMethod(beanName, mbd, args); 15 } 16 // 判断 是否重新创建同一bean时 17 boolean resolved = false; 18 boolean autowireNecessary = false; 19 if (args == null) { 20 synchronized (mbd.constructorArgumentLock) { 21 if (mbd.resolvedConstructorOrFactoryMethod != null) { 22 resolved = true; 23 autowireNecessary = mbd.constructorArgumentsResolved; 24 } 25 } 26 } 27 if (resolved) { 28 if (autowireNecessary) { 29 return autowireConstructor(beanName, mbd, null, null); 30 } 31 else { 32 return instantiateBean(beanName, mbd); 33 } 34 } 35 // 自动注入的构造器的,候选者 36 Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); 37 if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || 38 mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { 39 return autowireConstructor(beanName, mbd, ctors, args); 40 } 41 // 获得首选的构造器,可能是默认构造器 42 ctors = mbd.getPreferredConstructors(); 43 if (ctors != null) { 44 return autowireConstructor(beanName, mbd, ctors, null); 45 } 46 // 没有特别处理简单实用,无参构造器 47 return instantiateBean(beanName, mbd); 48} 49 50

我们可以看到,这个createBeanInstance ,有3种处理方式

  • instantiateUsingFactoryMethod 按照工厂方法进行实例化
  • autowireConstructor 构造函数注入, 构造器选择大致逻辑:
    1. 如果仅有默认构造器,没有指定参数,BeanDefinition中也没有构造器参数,使用默认构造方法。
    2. 如果有多个构造函数,对多个构造方法按照public优于private,同可见性的情况下,参数多的优于参数少的进行排序。一般采用宽松匹配模式(可以设置为严格匹配),优先选择参数原值类型匹配的,参数转换后类型匹配的次之,最后是都不匹配的。如果出现匹配度一致性的,选择第一个匹配的构造器。
  • instantiateBean 实例化bean,使用默认构造方法。

咱们看下instantiateBean 的逻辑

一般按照反射生成实例的方式如下方式:

1public static void main(String[] args) throws Exception{ 2 Class<CouponApplicationTests> clazz=CouponApplicationTests.class; 3 clazz.newInstance(); 4 Constructor constructorToUse = clazz.getDeclaredConstructor(); 5 constructorToUse.newInstance() 6} 7 8

Spring是如何实现的基本类似。查阅instantiateBean的源码发现,其核心逻辑委托给InstantiationStrategy的instantiate方法。Spring5.X的默认InstantiationStrategy 是CglibSubclassingInstantiationStrategy,而instantiate的实现并不在CglibSubclassingInstantiationStrategy中而是在,其父类SimpleInstantiationStrategy中。如下:

1@Override 2public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { 3 // Don't override the class with CGLIB if no overrides. 4 // 如果没有override方法, 即没有使用CGLIB重写 5 if (!bd.hasMethodOverrides()) { 6 Constructor<?> constructorToUse; 7 synchronized (bd.constructorArgumentLock) { 8 constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; 9 if (constructorToUse == null) { 10 final Class<?> clazz = bd.getBeanClass(); 11 if (clazz.isInterface()) { 12 throw new BeanInstantiationException(clazz, "Specified class is an interface"); 13 } 14 try { 15 if (System.getSecurityManager() != null) { 16 constructorToUse = AccessController.doPrivileged( 17 (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor); 18 } 19 else { 20 constructorToUse = clazz.getDeclaredConstructor(); 21 } 22 bd.resolvedConstructorOrFactoryMethod = constructorToUse; 23 } 24 catch (Throwable ex) { 25 throw new BeanInstantiationException(clazz, "No default constructor found", ex); 26 } 27 } 28 } 29 //就是执行constructorToUse.newInstance() 30 return BeanUtils.instantiateClass(constructorToUse); 31 } 32 else { 33 // Must generate CGLIB subclass. 34 return instantiateWithMethodInjection(bd, beanName, owner); 35 } 36} 37 38

如果没有MethodOverride,其大致逻辑,跟咱们写的差不多。 但是如果有MethodOverride则不同。
看到这里的时候我很好奇hasMethodOverrides和instantiateWithMethodInjection,看名称我猜测是对@Override注解进行处理,但是看了源码这个MethodOverrides是指replace-method和lookup-method,这个2配置,我没有使用过,查询了源码与网上的blog。简单说就是在这里利用Cglib实现方法注入。 对lookup-method和replace-method的说明您可以查阅

2.循环引用是什么?Spring 如何解决的?

什么是循环引用,举个列子:BeanA引用BeanB,BeanB也引用BeanA

1@Component 2class BeanA{ 3 @Autowired 4 BeanB beanB; 5} 6@Component 7class BeanB{ 8 @Autowired 9 BeanA beanA; 10}

Spring只能解决单例类型的循环,其解决帮扶就是提前暴露ObjectFactory,将未填充完属性的bean提前暴露出来。 流程图如下: Spring解决循环引用

啰嗦几句

写blog比看源码要费劲的多,我总是尝试使用生活中的例子比喻主流程,再加一点的补充说明。 以做菜比喻Spring源码,感觉自己源码学虽然不透彻,但是胃口好多了。

做菜喻源码
轮廓渐清晰
理解未透彻
肚子叫呱呱
美餐一顿去
下回咱再聊

populateBean下一篇解决 预计10月2号前

源码注释

1/** 2 * Actually create the specified bean. Pre-creation processing has already happened 3 * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks. 4 * <p>Differentiates between default bean instantiation, use of a 5 * factory method, and autowiring a constructor. 6 * 创建指定的Bean,此时创建前预处理已经执行了(查看postProcessBeforeInstantiation)。 7 * 区分默认bean实例化,工厂方法,并自动注入构造函数。 8 * @param beanName the name of the bean 9 * @param mbd the merged bean definition for the bean 10 * @param args explicit arguments to use for constructor or factory method invocation 11 * @return a new instance of the bean 12 * @throws BeanCreationException if the bean could not be created 13 * @see #instantiateBean 14 * @see #instantiateUsingFactoryMethod 15 * @see #autowireConstructor 16 */ 17protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) 18 throws BeanCreationException { 19 20 // Instantiate the bean. 21 // 1.实例化bean 22 BeanWrapper instanceWrapper = null; 23 if (mbd.isSingleton()) { 24 //Cache of unfinished FactoryBean instances 从FactoryBean name to BeanWrapper. 25 //移除未完成的bean的缓存中的实例 26 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); 27 } 28 if (instanceWrapper == null) { 29 //创建Bean实例 将BeanDefinition替换成BeanWrapper 30 instanceWrapper = createBeanInstance(beanName, mbd, args); 31 } 32 final Object bean = instanceWrapper.getWrappedInstance(); 33 Class<?> beanType = instanceWrapper.getWrappedClass(); 34 if (beanType != NullBean.class) { 35 mbd.resolvedTargetType = beanType; 36 } 37 // Allow post-processors to modify the merged bean definition. 38 synchronized (mbd.postProcessingLock) { 39 //Bean 40 if (!mbd.postProcessed) { 41 try { 42 //@AutoWired注解在这里,应用 43 //2.处理MergedBeanDefinitionPostProcessors 44 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); 45 } 46 catch (Throwable ex) { 47 throw new BeanCreationException(mbd.getResourceDescription(), beanName, 48 "Post-processing of merged bean definition failed", ex); 49 } 50 mbd.postProcessed = true; 51 } 52 } 53 // Eagerly cache singletons to be able to resolve circular references 54 // even when triggered by lifecycle interfaces like BeanFactoryAware. 55 // 3.允许提前暴露ObjectFactory,解决循环引用问题 56 // 单例,允许循环引用,对应的bean在创建 57 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && 58 isSingletonCurrentlyInCreation(beanName)); 59 if (earlySingletonExposure) { 60 if (logger.isTraceEnabled()) { 61 logger.trace("Eagerly caching bean '" + beanName + 62 "' to allow for resolving potential circular references"); 63 } 64 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 65 } 66 67 // Initialize the bean instance. 68 // 实例化bean 69 Object exposedObject = bean; 70 try { 71 //4.填充属性 72 populateBean(beanName, mbd, instanceWrapper); 73 //5.执行 init-method方法 74 exposedObject = initializeBean(beanName, exposedObject, mbd); 75 } 76 catch (Throwable ex) { 77 if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { 78 throw (BeanCreationException) ex; 79 } 80 else { 81 throw new BeanCreationException( 82 mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); 83 } 84 } 85 //6.有其他bean依赖当前完整bean(填充过属性),移除这些bean,有不能移除的抛出异常 86 if (earlySingletonExposure) { 87 Object earlySingletonReference = getSingleton(beanName, false); 88 if (earlySingletonReference != null) { 89 if (exposedObject == bean) { 90 exposedObject = earlySingletonReference; 91 } 92 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { 93 //不允许注入未被完全装载的bean,并且有其他Bean依赖当前这个Bean 94 String[] dependentBeans = getDependentBeans(beanName); 95 Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); 96 for (String dependentBean : dependentBeans) { 97 //移除依赖于当前Bean的其他bean 98 if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { 99 actualDependentBeans.add(dependentBean); 100 } 101 } 102 if (!actualDependentBeans.isEmpty()) { 103 //还有其他bean,依赖于当前Bean,未被移除 104 throw new BeanCurrentlyInCreationException(beanName, 105 "Bean with name '" + beanName + "' has been injected into other beans [" + 106 StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + 107 "] in its raw version as part of a circular reference, but has eventually been " + 108 "wrapped. This means that said other beans do not use the final version of the " + 109 "bean. This is often the result of over-eager type matching - consider using " + 110 "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); 111 } 112 } 113 } 114 } 115 // Register bean as disposable. 116 try { 117 //7.如果实现了 DisposableBean接口或者提供了destroy—method 在这里注册 118 registerDisposableBeanIfNecessary(beanName, bean, mbd); 119 } 120 catch (BeanDefinitionValidationException ex) { 121 throw new BeanCreationException( 122 mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); 123 } 124 return exposedObject; 125 }
点赞
收藏

评论区

加载中...

相关推荐

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Noark入门之WebSocket

支持WebSocket吗?你还在为H5的前端链接头疼吗?你还在了解WebSocket的握手协议吗?WebSocket有没有粘包概念啊?之前忘了说了,很不好意思,Noark在原来Tcp端口上实现了WebSocket协议的判定与处理,实现了Socket与WebSocket共存的效果还记得Socket链接服务器那一串暗号吗?那之前是为Flash

Twitter的分布式自增ID算法snowflake (Java版)

概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移

Host is not allowed to connect to this MySQL server

错误,数据库无法远程连接.第一步,关闭本地防火墙!(https://oscimg.oschina.net/oscnet/7afee6baf43336d38210f35df924ebf5c7d.png)注意:两台连接的机器都需要关闭第二步,两台机器互相ping,看是否可以互相访问!(https://images2018.cnb

Spring源码学习(四)在单值注入时如何按类型查找匹配的Bean

这是我学习Spring源码之后的第四篇文章,如果你想了解,之前的3篇文章请您查阅:前3篇blog的地址:1.Spring源码学习()别怕,老外点中餐与AbstractBeanFactory.getBean的主流程差不多(https://my.oschina.net/floor/blog/3106015)2.Spring源码学习