【小家Spring】Spring IoC容器中核心定义之------BeanDefinition深入分析(RootBeanDefinition、ChildBeanDefinition...)(上)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 【小家Spring】Spring IoC容器中核心定义之------BeanDefinition深入分析(RootBeanDefinition、ChildBeanDefinition...)(上)

前言


在前面分析Spring IoC容器的时候,贯穿全文的一个概念:Bean定义信息。它是Spring容器的一个核心概念,那么本文就深入分析一下BeanDefinition这个接口(类)。


Spring容器启动的过程中,会将Bean解析成Spring内部的BeanDefinition结构。

不管是是通过xml配置文件的\<Bean>标签,还是通过注解配置的@Bean,它最终都会被解析成一个Bean定义信息(对象),最后我们的Bean工厂就会根据这份Bean的定义信息,对bean进行实例化、初始化等等操作


从上可知BeanDefinition这个接口对Spring IoC容器的重要之处,所以了解好了它(以及子类),能让我们更大视野的来看Spring管理Bean的一个过程,也能透过现象看本质。


透彻理解Spring容器是打开Spring Boot大门的一把钥匙


下面用一个非常形象的比喻,来形容Spring IoC容器和BeanDefinition之前的关系。


比喻:BeanFactory和BeanDefinition


Spring IoC容器比作一间餐馆,当你来到餐馆,通常会直接招呼服务员:点菜!至于菜的原料是什么?如何用原料把菜做出来?可能你根本就不关心。

IoC容器也是一样,你只需要告诉它需要某个bean,它就把对应的实例(instance)扔给你,至于这个bean是否依赖其他组件,怎样完成它的初始化,根本就不需要你关心。

那么问题来了,作为餐馆,想要做出菜肴,得知道菜的原料和菜谱。同样地,IoC容器想要管理各个业务对象以及它们之间的依赖关系,需要通过某种途径来记录和管理这些信息。 BeanDefinition对象就承担了这个责任

容器中的每一个bean都会有一个对应的BeanDefinition实例,该实例负责保存bean对象的 所有 必要信息,包括bean对象的class类型、是否是抽象类、构造方法和参数、其它属性等等(所以BeanDefinition就好比做菜的原料)


需要说明的一点是:加入你是自己直接通过 SingletonBeanRegistry#registerSingleton向容器手动注入Bean的,那么就不会存在这份Bean定义信息的,这点需要注意。

Spring内部有不少这样的例子(因为这种Bean非常简单,根本不需要定义信息):

beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());

beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);

bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext);

bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME, Collections.unmodifiableMap(attributeMap));


现在可以开始做菜了吗?其实还不行,因为还没有菜谱(不知道做什么菜,怎么做~)

BeanDefinitionRegistry和 BeanFactory就是这份菜谱,BeanDefinitionRegistry抽象出bean的注册逻辑,而BeanFactory则抽象出了bean的管理逻辑

各个BeanFactory的实现类就具体承担了bean的注册以及管理工作


DefaultListableBeanFactory作为一个比较通用的BeanFactory实现,它同时也实现了BeanDefinitionRegistry接口,因此它就承担了Bean的注册管理工作


最后我们总结一下比喻关系:

  1. Spring IoC容器:餐馆(服务员)
  2. BeanDefinitionRegistry和 BeanFactory:菜谱
  3. BeanDefinitionRegistry:抽象出来的,向菜谱里注册菜(的管理器)
  4. BeanFactory:抽象出来的,管理这些菜谱(的管理器)
  5. BeanDefinition:原料(做菜所需要的原料)
  6. DefaultListableBeanFactory:具体实施者(具体注册菜谱、做菜的实施者)
  7. 依赖注入的使用者:客户(进店吃饭的人)


BeanDefinition源码分析


总体的 Java Class Diagrams 图:

image.png


因为它继承了AttributeAccessor,和BeanMetadataElement,所以我们先有必要来了解下这两个接口:


AttributeAccessor:定义了对对象元数据访问的抽象接口


// 接口都比较简单  就是定义了对对象属性的一些访问方法
//说明它可以持有Bean元数据元素,作用是可以持有XML文件的一个bean标签对应的Object(或者@Configuration元配置对象)
public interface AttributeAccessor {
  void setAttribute(String name, @Nullable Object value);
  @Nullable
  Object getAttribute(String name);
  @Nullable
  Object removeAttribute(String name);
  boolean hasAttribute(String name);
  String[] attributeNames();
}

AttributeAccessorSupport是唯一抽象实现,内部基于LinkedHashMap实现了所有的接口,供其他子类继承使用 主要针对属性CRUD操作


BeanMetadataElement:具有访问source(配置源)的能力


这个方法在@Configuration中使用较多,因为它会被代理

//接口提供了一个getResource()方法,用来传输一个可配置的源对象。
public interface BeanMetadataElement {
  /**
   * Return the configuration source {@code Object} for this metadata element
   * (may be {@code null}).
   * 返回元数据元素配置元对象
   */
  @Nullable
  Object getSource();
}

BeanDefinition:定义了Bean的各种信息


一个BeanDefinition描述了一个bean的实例,包括属性值,构造方法参数值和继承自它的类的更多信息。


BeanDefinition仅仅是一个最简单的接口,主要功能是允许BeanFactoryPostProcessor

例如PropertyPlaceHolderConfigure 能够检索并修改属性值和别的bean的元数据

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
  // 单例Bean还是原型Bean
  String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
  String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
  // Bean角色
  int ROLE_APPLICATION = 0; //应用程序重要组成部分
  int ROLE_SUPPORT = 1; //做为大量配置的一部分(支持、扩展类)  实际上就是说,我这个Bean是用户的,是从配置文件中过来的。
  int ROLE_INFRASTRUCTURE = 2; //指内部工作的基础构造  实际上是说我这Bean是Spring自己的,和你用户没有一毛钱关系
  // Modifiable attributes
  //parent definition(若存在父类的话,就设置进去)
  void setParentName(@Nullable String parentName);
  @Nullable
  String getParentName();
  // 指定Class类型。需要注意的是该类型还有可能被改变在Bean post-processing阶段
  // 若是getFactoryBeanName  getFactoryMethodName这种情况下会改变
  void setBeanClassName(@Nullable String beanClassName);
  @Nullable
  String getBeanClassName();
  //SCOPE_SINGLETON或者SCOPE_PROTOTYPE两种
  void setScope(@Nullable String scope);
  @Nullable
  String getScope();
  // @Lazy 是否需要懒加载(默认都是立马加载的)
  void setLazyInit(boolean lazyInit);
  boolean isLazyInit();
  // 此Bean定义需要依赖的Bean(显然可以有多个)
  void setDependsOn(@Nullable String... dependsOn);
  @Nullable
  String[] getDependsOn();
  // 这个Bean是否允许被自动注入到别的地方去(默认都是被允许的)
  // 注意:此标志只影响按类型装配,不影响byName的注入方式的~~~~
  void setAutowireCandidate(boolean autowireCandidate);
  boolean isAutowireCandidate();
  // 是否是首选的  @Primary
  void setPrimary(boolean primary);
  boolean isPrimary();
  // 指定使用的工厂Bean(若存在)的名称~
  void setFactoryBeanName(@Nullable String factoryBeanName);
  @Nullable
  String getFactoryBeanName();
  //指定工厂方法~
  void setFactoryMethodName(@Nullable String factoryMethodName);
  @Nullable
  String getFactoryMethodName();
  // 获取此Bean的构造函数参数值们  ConstructorArgumentValues:持有构造函数们的 
  // 绝大多数情况下是空对象 new ConstructorArgumentValues出来的一个对象
  // 当我们Scan实例化Bean的时候,可能用到它的非空构造,这里就会有对应的值了,然后后面就会再依赖注入了
  ConstructorArgumentValues getConstructorArgumentValues();
  default boolean hasConstructorArgumentValues() {
    return !getConstructorArgumentValues().isEmpty();
  }
  // 获取普通属性集合~~~~
  MutablePropertyValues getPropertyValues();
  default boolean hasPropertyValues() {
    return !getPropertyValues().isEmpty();
  }
  // Read-only attributes
  boolean isSingleton();
  boolean isPrototype();
  boolean isAbstract();
  // 对应上面的role的值
  int getRole();
  //@Description
  @Nullable
  String getDescription();
  // 返回该Bean定义来自于的资源的描述(用于在出现错误时显示上下文)
  @Nullable
  String getResourceDescription();
  //返回原始BeanDefinition,如果没有则返回@null
  // 若这个Bean定义被代理、修饰过  这个方法可以返回原始的
  @Nullable
  BeanDefinition getOriginatingBeanDefinition();
}

抽象实现、实现类们。上面已经画出了一些类的结构图,下面一个个来看


子接口:AnnotatedBeanDefinition


public interface AnnotatedBeanDefinition extends BeanDefinition {
  //获取该bean definition的注解元数据
  AnnotationMetadata getMetadata();
  //@since 4.1.1
  //Obtain metadata for this bean definition's factory method(如果不存在就返回null)
  @Nullable
  MethodMetadata getFactoryMethodMetadata();
}


AnnotationMetadata定义了访问特定类的注解的抽象接口,它不需要加载该类即可访问


该注解Bean定义旗下三大实现类:ScannedGenericBeanDefinition、ConfigurationClassBeanDefinition、AnnotatedGenericBeanDefinition


相关文章
|
11天前
|
Java 测试技术 开发者
IoC容器有什么作用?
【4月更文挑战第30天】IoC容器有什么作用?
30 0
|
11天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
21 0
|
11天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
21 0
|
11天前
|
Java 开发者 容器
IoC容器如何实现依赖注入?
【4月更文挑战第30天】IoC容器如何实现依赖注入?
21 0
|
11天前
|
XML Java 数据格式
如何配置IoC容器?
【4月更文挑战第30天】如何配置IoC容器?
19 0
|
11天前
|
XML Java 程序员
什么是Spring的IoC容器?
【4月更文挑战第30天】什么是Spring的IoC容器?
19 0
|
1天前
|
NoSQL Redis Docker
Mac上轻松几步搞定Docker与Redis安装:从下载安装到容器运行实测全程指南
Mac上轻松几步搞定Docker与Redis安装:从下载安装到容器运行实测全程指南
10 0
|
2天前
|
监控 Kubernetes Docker
【Docker 专栏】Docker 容器内应用的健康检查与自动恢复
【5月更文挑战第9天】本文探讨了Docker容器中应用的健康检查与自动恢复,强调其对应用稳定性和系统性能的重要性。健康检查包括进程、端口和应用特定检查,而自动恢复则涉及重启容器和重新部署。Docker原生及第三方工具(如Kubernetes)提供了相关功能。配置检查需考虑检查频率、应用特性和监控告警。案例分析展示了实际操作,未来发展趋势将趋向更智能和高效的检查恢复机制。
【Docker 专栏】Docker 容器内应用的健康检查与自动恢复
|
2天前
|
存储 安全 数据库
【Docker 专栏】Docker 容器内应用的状态持久化
【5月更文挑战第9天】本文探讨了Docker容器中应用状态持久化的重要性,包括数据保护、应用可用性和历史记录保存。主要持久化方法有数据卷、绑定挂载和外部存储服务。数据卷是推荐手段,可通过`docker volume create`命令创建并挂载。绑定挂载需注意权限和路径一致性。利用外部存储如数据库和云服务可应对复杂需求。最佳实践包括规划存储策略、定期备份和测试验证。随着技术发展,未来将有更智能的持久化解决方案。
【Docker 专栏】Docker 容器内应用的状态持久化
|
2天前
|
机器学习/深度学习 监控 Kubernetes
【Docker 专栏】Docker 容器内服务的自动扩展与缩容
【5月更文挑战第9天】本文探讨了Docker容器服务的自动扩展与缩容原理及实践,强调其在动态业务环境中的重要性。通过选择监控指标(如CPU使用率)、设定触发条件和制定扩展策略,实现资源的动态调整。方法包括云平台集成和使用Kubernetes等框架。实践中,电商平台和实时数据处理系统受益于此技术。注意点涉及监控数据准确性、扩展速度和资源分配。未来,智能算法将提升扩展缩容的效率和准确性,成为关键技术支持。
【Docker 专栏】Docker 容器内服务的自动扩展与缩容