文档章节

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

温安适
 温安适
发布于 09/21 17:18
字数 2268
阅读 919
收藏 4

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

入口

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

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

根据注释核心逻辑在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的源码:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   // 确认 bean的class,在这时已被解析了
   Class<?> beanClass = resolveBeanClass(mbd, beanName);
   if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
   }
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }

   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }
   // 判断 是否重新创建同一bean时
   boolean resolved = false;
   boolean autowireNecessary = false;
   if (args == null) {
      synchronized (mbd.constructorArgumentLock) {
         if (mbd.resolvedConstructorOrFactoryMethod != null) {
            resolved = true;
            autowireNecessary = mbd.constructorArgumentsResolved;
         }
      }
   }
   if (resolved) {
      if (autowireNecessary) {
         return autowireConstructor(beanName, mbd, null, null);
      }
      else {
         return instantiateBean(beanName, mbd);
      }
   }
   // 自动注入的构造器的,候选者
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      return autowireConstructor(beanName, mbd, ctors, args);
   }
   // 获得首选的构造器,可能是默认构造器
   ctors = mbd.getPreferredConstructors();
   if (ctors != null) {
      return autowireConstructor(beanName, mbd, ctors, null);
   }
   // 没有特别处理简单实用,无参构造器
   return instantiateBean(beanName, mbd);
}


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

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

咱们看下instantiateBean 的逻辑

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

public static void main(String[] args) throws Exception{
      Class<CouponApplicationTests> clazz=CouponApplicationTests.class;
      clazz.newInstance();
      Constructor constructorToUse = clazz.getDeclaredConstructor();
      constructorToUse.newInstance()
}


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

@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
   // Don't override the class with CGLIB if no overrides.
   // 如果没有override方法, 即没有使用CGLIB重写
   if (!bd.hasMethodOverrides()) {
      Constructor<?> constructorToUse;
      synchronized (bd.constructorArgumentLock) {
         constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
         if (constructorToUse == null) {
            final Class<?> clazz = bd.getBeanClass();
            if (clazz.isInterface()) {
               throw new BeanInstantiationException(clazz, "Specified class is an interface");
            }
            try {
               if (System.getSecurityManager() != null) {
                  constructorToUse = AccessController.doPrivileged(
                        (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
               }
               else {
                  constructorToUse = clazz.getDeclaredConstructor();
               }
               bd.resolvedConstructorOrFactoryMethod = constructorToUse;
            }
            catch (Throwable ex) {
               throw new BeanInstantiationException(clazz, "No default constructor found", ex);
            }
         }
      }
	  //就是执行constructorToUse.newInstance()
      return BeanUtils.instantiateClass(constructorToUse);
   }
   else {
      // Must generate CGLIB subclass.
      return instantiateWithMethodInjection(bd, beanName, owner);
   }
}


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

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

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

@Component
class BeanA{
   @Autowired
   BeanB beanB;
}
@Component
class BeanB{
   @Autowired
   BeanA beanA;
}

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

啰嗦几句

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

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

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

源码注释

/**
 * Actually create the specified bean. Pre-creation processing has already happened
 * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
 * <p>Differentiates between default bean instantiation, use of a
 * factory method, and autowiring a constructor.
 * 创建指定的Bean,此时创建前预处理已经执行了(查看postProcessBeforeInstantiation)。
 * 区分默认bean实例化,工厂方法,并自动注入构造函数。
 * @param beanName the name of the bean
 * @param mbd the merged bean definition for the bean
 * @param args explicit arguments to use for constructor or factory method invocation
 * @return a new instance of the bean
 * @throws BeanCreationException if the bean could not be created
 * @see #instantiateBean
 * @see #instantiateUsingFactoryMethod
 * @see #autowireConstructor
 */
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   // 1.实例化bean
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      //Cache of unfinished FactoryBean instances 从FactoryBean name to BeanWrapper.
      //移除未完成的bean的缓存中的实例
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      //创建Bean实例 将BeanDefinition替换成BeanWrapper
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   if (beanType != NullBean.class) {
      mbd.resolvedTargetType = beanType;
   }
   // Allow post-processors to modify the merged bean definition.
   synchronized (mbd.postProcessingLock) {
      //Bean
      if (!mbd.postProcessed) {
         try {
            //@AutoWired注解在这里,应用
            //2.处理MergedBeanDefinitionPostProcessors
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "Post-processing of merged bean definition failed", ex);
         }
         mbd.postProcessed = true;
      }
   }
   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
   // 3.允许提前暴露ObjectFactory,解决循环引用问题
   //  单例,允许循环引用,对应的bean在创建
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      if (logger.isTraceEnabled()) {
         logger.trace("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   // 实例化bean
   Object exposedObject = bean;
   try {
      //4.填充属性
      populateBean(beanName, mbd, instanceWrapper);
      //5.执行 init-method方法
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
         throw (BeanCreationException) ex;
      }
      else {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
      }
   }
   //6.有其他bean依赖当前完整bean(填充过属性),移除这些bean,有不能移除的抛出异常
   if (earlySingletonExposure) {
      Object earlySingletonReference = getSingleton(beanName, false);
      if (earlySingletonReference != null) {
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            //不允许注入未被完全装载的bean,并且有其他Bean依赖当前这个Bean
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
               //移除依赖于当前Bean的其他bean
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                  actualDependentBeans.add(dependentBean);
               }
            }
            if (!actualDependentBeans.isEmpty()) {
               //还有其他bean,依赖于当前Bean,未被移除
               throw new BeanCurrentlyInCreationException(beanName,
                     "Bean with name '" + beanName + "' has been injected into other beans [" +
                     StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                     "] in its raw version as part of a circular reference, but has eventually been " +
                     "wrapped. This means that said other beans do not use the final version of the " +
                     "bean. This is often the result of over-eager type matching - consider using " +
                     "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
            }
         }
      }
   }
   // Register bean as disposable.
   try {
      //7.如果实现了 DisposableBean接口或者提供了destroy—method 在这里注册
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }
   return exposedObject;
   }


© 著作权归作者所有

温安适

温安适

粉丝 118
博文 31
码字总数 51830
作品 0
朝阳
后端工程师
私信 提问
Spring源码学习(四)在单值注入时如何按类型查找匹配的Bean

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

温安适
10/03
150
0
自己手写一个SpringMVC框架

前端框架很多,但没有一个框架称霸,后端框架现在Spring已经完成大一统。所以学习Spring是Java程序员的必修课。 Spring框架对于Java后端程序员来说再熟悉不过了,以前只知道它用的反射实现的...

yzbty23
01/30
108
0
Spring源码学习(-)别怕,老外点中餐与AbstractBeanFactory.getBean的主流程差不多

引言 AbstractBeanFactory.getBean的流程,有点像老外点餐,不信咱们往下看。 入口 AbstractBeanFactory中有getBean的通用逻辑 核心逻辑在:doGetBean中。 逻辑说明: getBean流程 老外点餐 ...

温安适
09/14
202
0
自己手写一个SpringMVC框架(简化)

Spring框架对于Java后端程序员来说再熟悉不过了,以前只知道它用的反射实现的,但了解之后才知道有很多巧妙的设计在里面。如果不看Spring的源码,你将会失去一次和大师学习的机会:它的代码规...

我叫刘半仙
2018/02/22
13.2K
32
编程与下厨房:如何教女友写Python(二:不从Hello World开始

一、不从“Hello World”开始 但凡是介绍编程语言的入门书籍,都会把“hello world”这个句子的输出作为第一个程序的示例,这种约定俗成的做法就像是新居进火的仪式一般具有非凡的意义。 但是...

州的先生
2018/05/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

好程序员Java教程分享Zookeeper基本原理与运用场景

好程序员Java教程分享Zookeeper基本原理与运用场景一、什么是Zookeeper? zookeeper是一个分布式的一致性协调服务。 换句话说,也可以把zookeeper看成一个小型的分布式文件系统。但是和FastD...

好程序员官网
17分钟前
3
0
mysql表情符

1 修改表字段为utf8md4 ALTER table property_info MODIFY `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL 2 MySQL数据库服务器配置文件mysqld.cn......

干死it
37分钟前
3
0
正则表达式的基本语法

本文摘自LTP.NET知识库。 正则表达式的形式一般如下: /love/ 其中位于“/”定界符之间的部分就是将要在目标对象中进行匹配的模式。 用户只要把希望查找匹配对象的模式内容放入“/”定界符之...

木庄
39分钟前
3
0
java 框架有哪些?

十大常用框架: 一、SpringMVC 二、Spring 三、Mybatis 四、Dubbo 五、Maven 六、RabbitMQ 七、Log4j 八、Ehcache 九、Redis 十、Shiro 延展阅读: 一、SpringMVC Spring Web MVC是一种基于J...

java框架开发者
39分钟前
9
0
细谈Mysql事务

文章原创于公众号:程序猿周先森。本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号。 上一篇着重谈到了MySQL锁的概念,里面谈到了事务的概念,其实大部分开发者对于事务肯定不陌生,...

程序猿周先森
48分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部