Spring 源码(11)Spring Bean 的创建过程(2)

Spring Bean 的创建过程介绍了 FactoryBean 的创建方式,那么接下来介绍不是 FactoryBean的创建方式,在创建过程中,又会分为单例的Bean的创建,原型类型的Bean的创建等。一般来说在Spring中几乎所有对象都是单例创建的,除非有其他业务需要设置为其他作用域的Bean,所以重点以创建单例Bean为例。

单例Bean的创建

在创建时会调用 getBean,然后 doGetBean,一般来说在 Spring中只要是 do开头方法基本就是真正干活的方法,所以我们看 doGetBean方法的源码:

protected  T doGetBean(
  String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)
  throws BeansException {
  // 解析成规范的Bean name ,因为可能是FactoryBean加了& 前缀的Bean或者是有别名的Bean
  String beanName = transformedBeanName(name);
  Object bean;
  // Eagerly check singleton cache for manually registered singletons.

  // 获取缓存中的Bean
  Object sharedInstance = getSingleton(beanName);
  if (sharedInstance != null && args == null) {
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  }
  // 如果缓存中没有,那么就会按照单例或者多例的方式创建
  else {
    // 省略代码....

    // Check if bean definition exists in this factory.

    // 检查父类容器
    // 省略代码....

    if (!typeCheckOnly) {
      // 标记已经被创建
      markBeanAsCreated(beanName);
    }

    try {
      // 合并BeanDefinition
      RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      checkMergedBeanDefinition(mbd, beanName, args);

      // Guarantee initialization of beans that the current bean depends on.

      // 判断是否存在依赖的Bean的创建,比如dependsOn 依赖 A 这个Bean,那么就需要先创建A这个bean
      String[] dependsOn = mbd.getDependsOn();
      if (dependsOn != null) {
        for (String dep : dependsOn) {
          if (isDependent(beanName, dep)) {
            // 省略代码....

          }
          // 注册依赖的Bean,放在集合中
          registerDependentBean(dep, beanName);
          try {
            // 创建Bean
            getBean(dep);
          }
          catch (NoSuchBeanDefinitionException ex) {
           // 省略代码....

          }
        }
      }

      // Create bean instance.

      if (mbd.isSingleton()) {
        // 如果是单例的,就去创建Bean
        sharedInstance = getSingleton(beanName, () -> {
          try {
            // 创建Bean
            return createBean(beanName, mbd, args);
          }
          catch (BeansException ex) {
            // 省略代码....

          }
        });
        // 获取bean对象,会进行检查获取对象是否是FactoryBean
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      }
      // 原型作用的创建方式
      else if (mbd.isPrototype()) {
       // 省略代码....

      }
      else {
        // 省略代码....

      }
    }
    catch (BeansException ex) {
      // 省略代码....

    }
  }
  // 省略代码....

  return (T) bean;
}

去掉不重要的代码,可以看到首先是从缓存中获取,如果没有获取到就进行一些列检查,最终检查是否单例的 Bean,如果是,那么就会调用 getSingleton方法,传入一个 beanName,一个 ObjectFactorylambda表达式,表达式中有个 createBean方法,这个方法就是创建的 Bean方法。

那什么时候调用 crateBean方法呢?

答案是执行 lambda表达式的具体方法时执行,我们先看看这个 ObjectFactory接口是啥?

ObjectFactory 对象工厂

直接看源码:

@FunctionalInterface
public interface ObjectFactory {

    /**
     * Return an instance (possibly shared or independent)
     * of the object managed by this factory.
     * @return the resulting instance
     * @throws BeansException in case of creation errors
     */
    T getObject() throws BeansException;

}

这个接口是一个函数式接口,可以用于 lambda表达式直接使用,在调用 getObject方法时就是真正执行 lambda表达式中的方法。

具体看看 getSingleton方法的源码:

public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
  Assert.notNull(beanName, "Bean name must not be null");
  synchronized (this.singletonObjects) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
      if (this.singletonsCurrentlyInDestruction) {
        // 省略代码....

      }
      // 省略代码....

      // 检查 并添加正在创建的单例对象到集合中
      beforeSingletonCreation(beanName);
      // 设置为新的单例对象标识
      boolean newSingleton = false;
      // 设置异常集合,出现异常时将异常加入到集合中
      boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
      if (recordSuppressedExceptions) {
        this.suppressedExceptions = new LinkedHashSet<>();
      }
      try {
        // 执行具体的方法,调用crateBean方法
        singletonObject = singletonFactory.getObject();
        // 标识为新的单例对象
        newSingleton = true;
      }
      catch (IllegalStateException ex) {
        // 省略代码....

      }
      catch (BeanCreationException ex) {
        // 省略代码....

      }
      finally {
        if (recordSuppressedExceptions) {
          this.suppressedExceptions = null;
        }
        // 检查 并移除正在创建的单例对象
        afterSingletonCreation(beanName);
      }
      if (newSingleton) {
        // 加入到缓存中
        addSingleton(beanName, singletonObject);
      }
    }
    return singletonObject;
  }

这里执行完之后就会执行到 lambda表达式中的 createBean方法:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

   // 省略代码....

   RootBeanDefinition mbdToUse = mbd;

   // Make sure bean class is actually resolved at this point, and
   // clone the bean definition in case of a dynamically resolved Class
   // which cannot be stored in the shared merged bean definition.

   // 解析Bean的Class 用于反射创建对象
   Class resolvedClass = resolveBeanClass(mbd, beanName);
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }

   // Prepare method overrides.

   try {
      // 方法覆盖准备 lookup-method replace-method
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      // 省略代码....

   }

   try {
      // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.

      // 解析提前实例化,使用InstantiationAwareBeanPostProcessor实现
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if (bean != null) {
         return bean;
      }
   }
   catch (Throwable ex) {
      // 省略代码....

   }

   try {
      // 实例化 + 初始化 Bean
      // 真正的创建Bean
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      // 省略代码....

      return beanInstance;
   }
   catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      // 省略代码....

   }
   catch (Throwable ex) {
      // 省略代码....

   }
}

首先是进行了 Bean的类型的解析,主要是用于后面的反射创建对象时使用,并设置到 RootBeanDefinition中,然后进行方法覆盖操作。

Spring方法覆盖实战

方法覆盖就是使用了 lookup-methodreplace-method 标签的时候,就会进行方法的覆盖。方法覆盖有什么用处呢?

一般来说方法覆盖就是解决单例对象引用多例对象的时候使用方法覆盖。

做个实验试试:

定义一个房子类,用于停车

/**
 * @author redwinter
 * @since 1.0
 **/
public abstract class MyHouse {

    public abstract MyCar park();

}

定义我的车

/**
 * @author redwinter
 * @since 1.0
 **/
public interface MyCar {
    /**
     * 买一辆车
     * @return 车
     */
    MyCar buy();
}

定义实现类:

宝马车:

/**
 * @author redwinter
 * @since 1.0
 **/
public class BMW implements MyCar{

    @Override
    public MyCar buy() {
        return this;
    }

}

奔驰车:

/**
 * @author redwinter
 * @since 1.0
 **/
public class Ben implements MyCar{
    @Override
    public MyCar buy() {
        return this;
    }

}

xml配置:


测试类:

/**
 * @author redwinter
 * @since 1.0
 **/
public class LookupTest {

    /**
     * lookup-method 用来解决单例对象多例对象的
     */
    @Test
    public void lookupTest(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:method-override.xml");
        MyHouse myHouse = (MyHouse) context.getBean("myHouse1");
        MyHouse myHouse1 = (MyHouse) context.getBean("myHouse1");
        System.out.println(myHouse.park());
        System.out.println(myHouse1.park());
    }
}

输出:

com.redwinter.test.methodoverride.lookupmethod.BMW@4a765
com.redwinter.test.methodoverride.lookupmethod.BMW@3e6358

这里 Myhouse是一个单例的对象, myHouse1 调用的方法每次调用都是不同的对象。

Spring是如何实现方法覆盖的?

源码过于繁琐和复杂,这里直接看执行流程:

Spring 源码(11)Spring Bean 的创建过程(2)

Spring在加载 BeanDefinition的时候,执行 parseLookupOverrideSubElements 这个方法的时候只要设置了 lookup-method标签就会创建一个 LookupOverride类放入到 BeanDefinitionMethodOverrides 属性中,在进行 Bean的创建的时候,就会判断这个属性值是否有值,如果有那么就会在对象实例化时获取一个实例化策略,然后执行实例化,就、就会调用 SimpleInstantiationStrategy#instantiate 方法,然后使用 CGLIB进行实例化,创建出一个 Enhancer 增强类,并且设置一个回调类型为:

private static final Class[] CALLBACK_TYPES = new Class[]
                {NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};

最终在执行方法的时候就会调用到回调类 LookupOverrideMethodInterceptor 拦截器上,然后执行 Bean的创建:

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
  // Cast is safe, as CallbackFilter filters are used selectively.

  LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
  Assert.state(lo != null, "LookupOverride not found");
  Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
  if (StringUtils.hasText(lo.getBeanName())) {
    // 创建Bean
    Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
                   this.owner.getBean(lo.getBeanName()));
    // Detect package-protected NullBean instance through equals(null) check
    return (bean.equals(null) ? null : bean);
  }
  else {
    return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
            this.owner.getBean(method.getReturnType()));
  }
}

这篇文章就介绍到这里,下一篇继续。

Original: https://www.cnblogs.com/redwinter/p/16255297.html
Author: 玲丶蹊
Title: Spring 源码(11)Spring Bean 的创建过程(2)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/610038/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 64位虚拟机Guest OS安装错误:0xC0000225

    1, CPU必须支持Intel VT-x/AMD-V。并且在BIOS及虚拟机软件中启用。 2, 虚拟机要启用 IO ACPI。 Original: https://www.cnbl…

    Java 2023年5月30日
    074
  • IDEA超级好用的插件推荐

    IDEA超级好用的插件推荐 以下都是本人使用idea开发以来,所使用过的插件,强烈推荐,提升代码质量,事半功倍之首选!!! 先介绍下如何安装这些插件:(本人使用idea的版本是20…

    Java 2023年6月15日
    086
  • Nginx转发地址解决跨域问题

    什么是跨域问题 在一个服务器A里放置了json文件,另一个服务器B想向A发送ajax请求,获取此文件,会发生错误。 Chrome提示: XMLHttpRequest cannot …

    Java 2023年5月30日
    074
  • 当发现你的OpenStack虚拟机网络有问题,不妨先试一下这16个步骤

    1. Security Group全部打开,这是最基本的,但是很多人容易忘记 其实遇到过无数这种场景了,Debug了半天网络问题,各种手段都用上了,最后发现安全组竟然没有打开。 2…

    Java 2023年5月30日
    0107
  • 将一个数随机拆分成多个整数

    2 * 将一个数随机拆分为多&#x4E2…

    Java 2023年6月6日
    094
  • Elasticsearch 入门实战(4)–Java Low Level REST Client 使用

    本文主要介绍 Elasticsearch Java Low Level REST Client 的使用,相关的环境及软件信息如下:CentOS 7.6.1810、Java 1.8….

    Java 2023年6月16日
    094
  • java 双因素认证(2FA)TOTP demo

    TOTP 的全称是”基于时间的一次性密码”(Time-based One-time Password)。它是公认的可靠解决方案,已经写入国际标准 RFC62…

    Java 2023年6月16日
    094
  • 重看Maven技术

    我使用Maven主要用于包管理。 安装,配置环境变量这些就不说了。 Maven提供的标准目录结构 Maven3的目录结构如下图所示 src -main     –java java…

    Java 2023年6月5日
    074
  • 字符串中实用的方法

    1、strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串,当strtok()在参数s的字符串中发现到参数delim的分割字符时则会将…

    Java 2023年5月29日
    093
  • 数组判断任意出现的重复值

    1、题目背景 给你一个整数数组nums,如果任一值在数组中出现至少两次,返回true;如果数组中每个元素互不相同,返回false 2、代码实现 public class Solut…

    Java 2023年6月8日
    084
  • 面向对象—类与类的关系

    package com.gao.test.Test5; public class Girl { //属性 String name; // friend f; //与生俱来就有朋友(…

    Java 2023年6月5日
    083
  • 手撕spring核心源码,彻底搞懂spring流程

    引子 十几年前,刚工作不久的程序员还能过着很轻松的日子。记得那时候公司里有些开发和测试的女孩子,经常有问题解决不了的,不管什么领域的问题找到我,我都能帮她们解决。但是那时候我没有主…

    Java 2023年5月30日
    0102
  • 800java面试题

    800java面试题1、meta标签的作用是什么2、ReenTrantLock可重入锁(和synchronized的区别)总结3、Spring中的自动装配有哪些限制?4、什么是可变…

    Java 2023年5月29日
    087
  • 用NginX+keepalived实现高可用的负载均衡

    经过前面的配置,如果主服务器的keepalived停止服务,从服务器会自动接管VIP对外服务;一旦主服务器的keepalived恢复,会重新接管VIP。 但这并不是我们需要的,我们…

    Java 2023年5月30日
    082
  • JavaSE_lambda表达式 Stream流 Option类

    对Java_lambda表达式 Stream流 Option类进行总结和简要介绍 1 Lambda表达式 1.1 作用 为了简化匿名内部类的书写 1.2 思想 函数式编程思想 : …

    Java 2023年6月9日
    084
  • 虚拟机vmware实现共享文件夹hgfs

    VMware中的centos共享目录/mnt/hgfs 首先,在vmware上手动配置 执行命令 sudo vmhgfs-fuse .host:/ /mnt/hgfs -o non…

    Java 2023年5月30日
    088
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球