前言
在前面分析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的注册管理工作
最后我们总结一下比喻关系:
- Spring IoC容器:餐馆(服务员)
- BeanDefinitionRegistry和 BeanFactory:菜谱
- BeanDefinitionRegistry:抽象出来的,向菜谱里注册菜(的管理器)
- BeanFactory:抽象出来的,管理这些菜谱(的管理器)
- BeanDefinition:原料(做菜所需要的原料)
- DefaultListableBeanFactory:具体实施者(具体注册菜谱、做菜的实施者)
- 依赖注入的使用者:客户(进店吃饭的人)
BeanDefinition源码分析
总体的 Java Class Diagrams 图:
因为它继承了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