Spring源码:Bean的生命周期(二)

前言

让我们继续讲解Spring的Bean实例化过程。在上一节中,我们已经讲解了Spring是如何将Bean定义加入到IoC容器中,并使用合并的Bean定义来包装原始的Bean定义。接下来,我们将继续讲解Spring的 getBean() 方法,特别是针对 FactoryBean 的解析。

getBean() 方法中,Spring还支持对 FactoryBean 进行特殊处理。FactoryBean 是一个能够生成Bean实例的工厂Bean,其定义了 getObject() 方法,返回的是一个由工厂Bean管理的对象实例。在使用 getBean() 方法获取 FactoryBean 类型的Bean时,Spring会首先获取 FactoryBean 的实例,然后调用其 getObject() 方法来获取由工厂Bean创建的实际Bean实例。

因此,在使用 getBean() 方法获取Bean实例时,我们需要注意是否需要对 FactoryBean 进行特殊处理。如果需要获取 FactoryBean 的实例而不是它所管理的对象实例,可以在Bean名称前加上 & 符号来进行标识。例如:&myFactoryBean 表示获取 myFactoryBean 的实例。但是博主看到第一篇源码写的篇幅确实有些长,可能对于大家伙的碎片化时间掌握的不是很充分,所以以后我会尽力控制篇幅长度,既保证逻辑的连续性也保证尽快可以看完,那么接下来开始进入正题getbean方法之FactoryBean解析。

FactoryBean

所有符合过滤条件的Bean在Spring解析后都会被转化为合并后的Bean定义。尽管Spring提供了 getBean() 方法用于获取Bean实例,但实际上它底层仍然使用 createBean() 方法来创建Bean实例。在创建Bean实例之前,Spring先对当前Bean定义进行判断,以确定其是否为 FactoryBean 类型:

public void preInstantiateSingletons() throws BeansException {  
   if (logger.isTraceEnabled()) {  
      logger.trace("Pre-instantiating singletons in " + this);  
   }  
  
   // Iterate over a copy to allow for init methods which in turn register new bean definitions.  
 // While this may not be part of the regular factory bootstrap, it does otherwise work fine.  List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);  
  
   // Trigger initialization of all non-lazy singleton beans...  
  for (String beanName : beanNames) {  
      // 获取合并后的BeanDefinition  
  RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);  
  
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {  
         if (isFactoryBean(beanName)) {  
            // 获取FactoryBean对象  
  Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);  
            if (bean instanceof FactoryBean) {  
               FactoryBean<?> factory = (FactoryBean<?>) bean;  
               boolean isEagerInit;  
               if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {  
                  isEagerInit = AccessController.doPrivileged(  
                        (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,  
                        getAccessControlContext());  
               }  
               else {  
                  isEagerInit = (factory instanceof SmartFactoryBean &&  
                        ((SmartFactoryBean<?>) factory).isEagerInit());  
               }  
               if (isEagerInit) {  
                  // 创建真正的Bean对象(getObject()返回的对象)  
  getBean(beanName);  
               }  
            }  
         }  
         else {  
            // 创建Bean对象  
  getBean(beanName);  
         }  
      }  
   }  
  
   // 所有的非懒加载单例Bean都创建完了后  
  // Trigger post-initialization callback for all applicable beans...  
  for (String beanName : beanNames) {  
      Object singletonInstance = getSingleton(beanName);  
      if (singletonInstance instanceof SmartInitializingSingleton) {  
         StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")  
               .tag("beanName", beanName);  
         SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;  
         if (System.getSecurityManager() != null) {  
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {  
               smartSingleton.afterSingletonsInstantiated();  
               return null;  
            }, getAccessControlContext());  
         }  
         else {  
            smartSingleton.afterSingletonsInstantiated();  
         }  
         smartInitialize.end();  
      }  
   }  
}

他的源码逻辑大致如下:

  1. Spring会根据 beanNamebean 定义 Map 中获取当前合并的 Bean 定义。
  2. Spring会对当前 Bean 定义进行判断,包括判断当前 Bean 是否为抽象的、是否为单例、是否懒加载,以及是否为 FactoryBean。如果是 FactoryBean,则会走 FactoryBean 的创建逻辑,否则会走单例 Bean 的创建逻辑。
  3. 当所有单例非懒加载的 Bean 创建完成后,Spring会遍历所有单例 Bean,判断其是否为 SmartInitializingSingleton 类型。如果是,则会自动调用 afterSingletonsInstantiated 方法。

isFactoryBean

由于创建 Bean 的逻辑比较复杂,其中包含了许多细节,因此,在这里我们特别提到了一个方法 isFactoryBean()。之所以要提到这个方法,是因为Spring支持使用 FactoryBean 来创建复杂对象。下面是该方法的主要源码:

public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {  
   String beanName = transformedBeanName(name);  
   Object beanInstance = getSingleton(beanName, false);  
   if (beanInstance != null) {  
      return (beanInstance instanceof FactoryBean);  
   }  
   // No singleton instance found -> check bean definition.  
  if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {  
      // No bean definition found in this factory -> delegate to parent.  
  return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);  
   }  
   return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));  
}

大致逻辑如下:

  1. transformedBeanName 的作用是不管传入的参数是 &××× 还是 ×××,都返回 ×××。这是因为Spring标记 FactoryBean 时使用 &××× 作为 FactoryBeanbeanName
  2. getSingleton 方法从单例池中获取 Bean 实例,如果该实例是 FactoryBean,则直接返回该实例。
  3. 如果 BeanFactory 中的 Bean 定义 Map 中不包含该 beanNameBean 定义,并且当前 BeanFactory 的父 BeanFactory 实现了 ConfigurableBeanFactory 接口,那么就需要查看当前父 BeanFactory 中是否有该实例,并且判断该实例是否为 FactoryBean。举个例子来说:
// 创建一个父Spring容器  
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();  
parent.register(AppConfig.class);  
parent.refresh();  
// 创建一个Spring容器  
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();  
applicationContext.setParent(parent);  
applicationContext.register(AppConfig1.class);  
applicationContext.refresh();  
UserService bean = applicationContext.getBean(UserService.class);  
bean.test();
  1. 如果并没有实例化出来的bean,那么对bean定义进行判断。
protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {  
   Boolean result = mbd.isFactoryBean;  
   if (result == null) {  
      // 根据BeanDefinition推测Bean类型(获取BeanDefinition的beanClass属性)  
  Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);  
      // 判断是不是实现了FactoryBean接口  
  result = (beanType != null && FactoryBean.class.isAssignableFrom(beanType));  
      mbd.isFactoryBean = result;  
   }  
   return result;  
}

注释也基本写好了,基本上就是根据BeanDefinition推测Bean类型(获取BeanDefinition的beanClass属性),再根据bean类型判断是不是实现了FactoryBean接口,然后返回判断结果。

SmartFactoryBean

getBean 方法中,我们可以获取 FactoryBean 的实例并返回。接下来的步骤是判断当前的 FactoryBean 是否实现了 SmartFactoryBean 接口。需要注意的是,SmartFactoryBeanFactoryBean 接口的一个子接口。虽然我们在实现 FactoryBean 接口时不必实现 SmartFactoryBean 接口,但是如果实现了 SmartFactoryBean 接口,那么在创建 FactoryBean 时就会调用 getObject 方法返回实例。正常情况下,只有当容器启动完成后才会调用 getObject 方法。如果我们想在初始化时就调用,可以这样实现:

@Component  
public class UserFactory implements SmartFactoryBean {  
 
   @Override  
  public Object getObject() throws Exception {  
      return new User();  
   }  
  
   @Override  
  public Class<?> getObjectType() {  
      return User.class;  
   }  
  
   @Override  
  public boolean isEagerInit() {  
      return true;  
   }  
}

结语

FactoryBean 和 BeanFactory 是两个不同的概念。前者是一个接口,我们可以在实现该接口时通过调用 getObject 方法来返回实例,同时 FactoryBean 本身也是一个实例。后者是 Spring 容器的工厂,通过其中的 bean 定义 Map 一个一个地实例化我们通过注解等方式注入进去的 bean 工厂。在判断 FactoryBean 时,如果当前 BeanFactory 中没有对应的 bean 定义,那么就会去父容器中寻找相应的 bean 定义并进行判断。如果我们的类实现了 SmartFactoryBean 接口,那么它将会在 Spring 容器启动时就会调用 getObject 方法创建实例。接下来,我们将分几个小节来讲解 getBean 方法是如何实例化 bean 的,因为篇幅过长会影响读者的注意力和学习效果。

公众号

原文链接:https://www.cnblogs.com/guoxiaoyu/p/17367019.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring源码:Bean的生命周期(二) - Python技术站

(0)
上一篇 2023年5月1日
下一篇 2023年5月2日

相关文章

  • java初学者如何让编程学习起来更简单

    这里提供一些帮助Java初学者更轻松学习编程的攻略: 1. 选择适合自己的学习方法 学习方法的选择对于学习编程语言来说非常重要。有的人更喜欢以视频教程和示例代码为主,而有些人则更喜欢以书本为主。此外,还有一些适用于不同学习风格的在线课程,例如交互式课程和mooc(大规模开放式在线课程)。初学者应该探索各种不同的学习途径,找出自己最适合的一种。 2. 坚持练习…

    Java 2023年5月19日
    00
  • 将json当数据库一样操作的javascript lib

    将JSON当做数据库一样操作的JavaScript库,可以让我们用JavaScript快速地进行数据存储和读取。下面是使用JSON来操作数据的完整攻略。 1. 使用JSON来模拟数据库 JSON格式的数据结构与关系型数据库相似,拥有表格、列和行,可以在内存中保存和读取数据。我们可以使用JSON数据结构来模拟一个数据库。 首先,创建一个JSON文件,并在其中定…

    Java 2023年5月26日
    00
  • java7 新I/O知识点详解

    Java7 新 I/O 知识点详解 介绍 Java7 引入了一些新的 I/O(输入输出)特性,主要是为了优化文件 I/O 操作,使之更加高效和灵活。其中主要包括以下几个方面: 支持异步 I/O 操作的 NIO API 支持读取和写入字符串的 NIO API 自动资源管理(ARM)特性,即 try-with-resources 操作 文件系统的改进 下面将分别…

    Java 2023年5月24日
    00
  • java日期操作工具类(获取指定日期、日期转换、相隔天数)

    Java日期操作工具类 在Java中进行日期操作比较麻烦,需要经常进行格式化、计算等操作。使用Java日期操作工具类可以简化这些操作,提高代码可读性和可维护性。本文将介绍如何使用Java日期操作工具类完成获取指定日期、日期转换和相隔天数等常用操作。 获取指定日期 在Java日期操作中,通常需要获取当前日期、指定日期、某个日期的前一天或后一天等。下面是Java…

    Java 2023年5月20日
    00
  • Java实现冒泡排序算法

    当需要对一个数组(或者列表)进行排序时,冒泡排序是最基本的一种排序算法之一。下面详细讲解Java实现冒泡排序算法的完整攻略。 定义 “冒泡排序”指的是通过不断地比较相邻的元素,并交换不合适的元素位置,从而逐步将无序的元素移动到正确的位置。它的过程像气泡不断从水中升起,因此得名“冒泡排序”。 实现 下面是Java实现冒泡排序的示例代码: public stat…

    Java 2023年5月19日
    00
  • 详解SpringBoot2 使用Spring Session集群

    详解SpringBoot2 使用Spring Session集群攻略 什么是Spring Session Spring Session是一个支持在不同Web容器之间共享Session数据的项目。 Spring Session的集群 在集群环境下,我们需要使用Spring Session来共享Session数据。具体实现方式如下: 引入Spring Sessi…

    Java 2023年5月19日
    00
  • Log4j新手快速入门教程

    Log4j新手快速入门教程攻略 介绍 Log4j是Java中广泛使用的开源日志记录组件。它可以将应用程序的日志输出到控制台、文件或网络,并可通过配置文件进行灵活的日志输出控制。本文将介绍Log4j的基本概念、使用方法和配置文件的格式,以帮助新手快速入门。 基本概念 Log4j提供了三个基本概念:Logger、Appender和Layout。 Logger:日…

    Java 2023年5月26日
    00
  • 关于Java中Json的各种处理

    Java中Json的处理攻略 Json是什么? JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。JSON格式可以用来描述“键值对”(key-value)类别的数据,通常用于Web服务与Web程序之间的数据交换。 Java中Json的处理方法 1. 使用JSON库 Java中有很多处理JSON的库,比如Gson、Ja…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部