草庐IT

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

努力的小雨 2024-06-26 原文

前言

让我们继续讲解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 的,因为篇幅过长会影响读者的注意力和学习效果。

有关Spring源码:Bean的生命周期(二)的更多相关文章

  1. ruby-on-rails - 带 Spring 锁的 Rails 4 控制台 - 2

    我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.

  2. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  3. spring.profiles.active和spring.profiles.include的使用及区别说明 - 2

    转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev

  4. ruby-on-rails - Spring 不起作用。 [未初始化常量 Spring::SID::DL] - 2

    我无法运行Spring。这是错误日志。myid-no-MacBook-Pro:myid$spring/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/lib/spring/sid.rb:17:in`fiddle_func':uninitializedconstantSpring::SID::DL(NameError)from/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/li

  5. elasticsearch源码关于TransportSearchAction【阶段三】 - 2

    1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>

  6. (附源码)vue3.0+.NET6实现聊天室(实时聊天SignalR) - 2

    参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍  介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。  内容有:    ①:Hub模型的方法介绍    ②:服务器端代码介绍    ③:前端vue3安装并调用后端方法    ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke()  去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on

  7. 【云原生】SpringCloud-Spring Boot Starter使用测试 - 2

    目录SpringBootStarter是什么?以前传统的做法使用SpringBootStarter之后starter的理念:starter的实现: 创建SpringBootStarter步骤在idea新建一个starter项目、直接执行下一步即可生成项目。 在xml中加入如下配置文件:创建proterties类来保存配置信息创建业务类:创建AutoConfiguration测试如下:SpringBootStarter是什么? SpringBootStarter是在SpringBoot组件中被提出来的一种概念、简化了很多烦琐的配置、通过引入各种SpringBootStarter包可以快速搭建出一

  8. Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理) - 2

    快速导航(持续更新中…)Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理)Cesium源码解析二(metadataAvailability的含义)Cesium源码解析三(metadata元数据拓展中行列号的分块规则解析)Cesium源码解析四(Quantized-Mesh(.terrain)格式文件在CesiumJS和UE中加载情况的对比)目录1.前言2.本篇的由来3.terrain文件的加载3.1更新环境3.2更新和执行渲染命令3.3数据优化3.4结束当前帧4.总结1.前言  目前市场上三维比较火的实现方案主要有两种,b/s的方案主要是Cesium,c/s的方案主要是u

  9. Spring Boot集成ElasticSearach - 2

    文章目录前言一、Elasticsearch版本介绍二、客户端种类三、客户端与版本兼容性四、引入Elasticsearch依赖包五、客户端配置六、Elasticsearch使用前言ElasticSearch是Elastic公司出品的一款功能强大的搜索引擎,被广泛的应用于各大IT公司,它的代码位于https://github.com/elastic/elasticsearch,目前是一个开源项目。ElasticSearch公司的另外两个开源产品Logstash、Kibana与ElasticSearch构成了著名的ELK技术栈。。他们三个共同形成了一个强大的生态圈。简单地说,Logstash负责数据

  10. Spring Security 6.0系列【32】授权服务器篇之默认过滤器 - 2

    有道无术,术尚可求,有术无道,止于术。本系列SpringBoot版本3.0.4本系列SpringSecurity版本6.0.2本系列SpringAuthorizationServer版本1.0.2源码地址:https://gitee.com/pearl-organization/study-spring-security-demo文章目录前言1.OAuth2AuthorizationServerMetadataEndpointFilter2.OAuth2AuthorizationEndpointFilter3.OidcProviderConfigurationEndpointFilter4.N

随机推荐