什么是 BeanDefinition?

BeanDefinition 直译为 bean 定义,描述了一个 bean 实例具有的构造方法参数和属性值等信息。与 Java 中的 Class 类似,Class 是类文件在内存中的表现形式,BeanDefinition 是 Spring Bean 配置元信息在内存中的表现形式,各种配置元信息最后都会被转换为 BeanDefinition ,Spring 根据 BeanDefinition 实例化、初始化 bean,BeanDefinition 涉及到 Spring Bean 的整个生命周期。

BeanDefinition 的分类及使用场景

在 Spring 3 之前,我们最常用 xml 配置 Spring 中的 bean,Spring 3 及之后,注解逐渐成为使用 Spring 的主要方式。此外较少使用的 bean 配置元数据还包括 properties、groovy,我们甚至可以定义自己的 BeanDefinition。Spring 中的 BeanDefinition 主要如下图所示。

spring 修改请求方式 spring修改beandefinition_spring

BeanDefinition 是所有 BeanDefinition 实现类的接口,其定义了基本的 BeanDefinition 信息,各实现可以包含其他不同的信息。BeanDefinition 包含的信息及含义如下所示。

属性

含义

作用

parentName

父 BeanDefinition 名称

BeanDefinition 具有父子关系,子 BeanDefinition 可以继承父 BeanDefinition,并可以覆盖父 BeanDefinition 中的信息

beanClassName

bean 的类名

可能包含占位符,不一定是运行时 bean 的类型

scope

作用域

bean 的作用范围,如 singleton、prototype 及自定义的 scope 等

lazyInit

延迟初始化

默认非延迟初始化,在容器启动后会获取非延迟初始化的单例 bean

dependsOn

依赖的其他 bean

当前 bean 如果需要 实例化,则必须保证依赖的 bean 先进行实例化

autowireCandidate

是否为候选对象

如果不为候选对象,则 autowire 时当前 bean 不会被注入

primary

是否为主要的 bean

如果为 primary,则存在多个相同类型的 bean 时,primary bean 会被优先 autowire

factoryBeanName

工厂 bean 名称

用于指定创建当前 bean 的其他 bean 实例

factoryMethodName

工厂方法名称

用于指定创建当前 bean 的 bean 实例的方法。factoryBeanName 此时如果为空则工厂 bean 为当前 bean ,工厂方法必须为静态方法。

constructorArgumentValues

构造方法参数值

表示用于创建 bean 的构造方法或工厂方法的参数值,Spring 会根据参数选择合适的构造方法

propertyValues

bean 的属性值

bean 实例化后会根据属性值进行初始化

initMethodName

初始化方法名称

bean 实例化后会进行初始化,调用初始化方法,该方法不能包含参数

destroyMethodName

销毁方法名称

bean 实例在销毁之前调用的方法,该方法最多携带一个 boolean 类型的参数

role

角色

表示 bean 在应用中扮演的角色

description

描述

表示 bean 的描述信息

resolvableType

可解析类型

表示 bean 在运行时的实际类型,包含泛型信息

singleton

单例

表示当前 bean 是否为单例,即在某一容器内只能有一个实例

prototype

原型

每次获取 bean 的实例,都会创建一个新的实例

abstract

抽象

当前 BeanDefinition 是否为抽象的,如果为抽象的则不能用来实例化 bean

resourceDescription

资源描述

加载当前 BeanDefinition 的资源的描述信息

originatingBeanDefinition

原始 BeanDefinition

一个 BeanDefinition 可以作为其他 BeanDefinition 的代理,其表示代理的对象

AbstractBeanDefinition 是 BeanDefinition 的抽象实现,并在 BeanDefinition 接口的基础上添加了一些获取 BeanDefinition 其他信息的方法。

RootBeanDefinition 是合并后的 BeanDefinition,由于 BeanDefinition 可以具有父子关系,因此在运行时需要获取最终表示 bean 的 RootBeanDefinition ,其不能再获取父 BeanDefinition。

ChildBeanDefinition ,表示具有 父 BeanDefinition 的 BeanDefinition,使用较少。

GenericBeanDefinition,通用的 BeanDefinition,Spring 推荐编程式注册 BeanDefinition 时使用 GenericBeanDefinition,而不是使用 RootBeanDefinition。

AnnotatedBeanDefinition,注解 BeanDefinition,Spring 注解编程中使用的 BeanDefinition,包含类或方法的注解元信息。

如何创建 BeanDefinition

通常情况下,Spring 在解析不同的配置元信息时会使用不同的 BeanDefinition,我们不必进行关心。而如果我们想通过编程式向 Spring 注册 BeanDefinition ,我们则需要掌握创建 BeanDefinition 的一些技巧。具有两种创建 BeanDefinition 的方式。

  • 直接通过 new 指令创建 BeanDefinition。
  • 通过 BeanDefinitionBuilder 的静态方法创建 BeanDefinition ,这是建造者模式的一种实现,推荐使用这种方式创建 BeanDefinition。示例代码如下所示:
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(String.class)
        .setLazyInit(true).setAbstract(false).getBeanDefinition();

BeanDefinition 解析

BeanDefinition 通过 BeanDefinitionReader 解析配置元数据获取,BeanDefinitionReader 根据不同的配置元数据具有不同的实现,具体如下。

spring 修改请求方式 spring修改beandefinition_spring_02


根据如上类图,可以发现,除了 BeanDefinitionReader 接口,还具有一个用于解析注解元数据的 AnnotatedBeanDefinitionReader ,这是因为 BeanDefinitionReader 解析的都是资源文件,而 AnnotatedBeanDefinitionReader 是 Spring 3 才添加的用来解析注解元数据为 BeanDefinition。

AbstractBeanDefinitionReader 是 BeanDefinitionReader 的抽象实现,主要将资源位置解析为资源 Resource。 PropertiesBeanDefinitionReader 用来解析 properties 文件,XMLBeanDefinitionReader 用来解析 xml 文件,GrovvyBeanDefinitionReader 用来解析 grovvy 文件。

目前最常用的为注解,在老旧的项目中可能存在 xml 配置文件,而 properties 和 groovy 则几乎没有用户使用。

注解元数据解析

AnnotatedBeanDefinitionReader 解析注解元数据为 BeanDefinition 的核心源码如下。

private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
			@Nullable BeanDefinitionCustomizer[] customizers) {

		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
		// 先进行 @Conditional 条件评估
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(supplier);
		// 解析 scope 信息
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
		// 处理通用的注解信息,如 @Primary、@Lazy 等
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) {
			// 处理 qualifier 信息
			for (Class<? extends Annotation> qualifier : qualifiers) {
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		// 自定义 BeanDefinition 信息
		if (customizers != null) {
			for (BeanDefinitionCustomizer customizer : customizers) {
				customizer.customize(abd);
			}
		}
		// 创建 BeanDefinition
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		// 注册 BeanDefinition
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}

可以看到,AnnotatedBeanDefinitionReader 实现就是读取类的配置信息,将其转换为对应的 BeanDefinition 属性,然后再创建 BeanDefinition 。需要注意的是,在扫描包时会使用 MetadataReader 读取类,底层使用 asm 读取类,因此不会进行类加载,会将类的元信息抽象为 AnnotationMetadata,此时直接创建 ScannedGenericBeanDefinition 对象。

xml 配置解析

xml 配置文件的解析由 XmlBeanDefinitionReader 来完成。XmlBeanDefinitionReader 加载 BeanDefinition 的入口方法如下。

@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

这里正是将前面提到的 AbstractBeanDefinitionReader 解析出的资源文件作为参数,然后转换为 EncodedResource。继续跟踪源码如下。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		... 省略部分代码
		try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
			InputSource inputSource = new InputSource(inputStream);
			if (encodedResource.getEncoding() != null) {
				inputSource.setEncoding(encodedResource.getEncoding());
			}
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}
		... 省略部分代码
	}

这里将 EncodedResource 转换为 InputSource ,继续跟踪源码如下。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			Document doc = doLoadDocument(inputSource, resource);
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		... 省略部分代码
	}

先把 InputSource 转换为 Document,然后解析 Document 注册 bean。解析 xml 是使用 jdk org.xml.sax 进行完成,查看 doLoadDocument 方法如下。

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
		return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
				getValidationModeForResource(resource), isNamespaceAware());
	}

这里有一个 EntityResolver 需要留意,其用来获取本地的 dtd 或 xsd ,避免网络请求的开销或网络请求失败。主要的实现为 DelegatingEntityResolver,其会将 dtd 文件的获取委托为 BeansDtdResolver,将 xsd 文件的获取委托给 PluggableSchemaResolver。PluggableSchemaResolver 会从类路径下 META-INF/spring.schemas文件读取 xsd 文件的位置。spring-beans 模块下 spring.schemas 的部分内容如下。

http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util.xsd

跟踪注册 BeanDefinition 的方法如下。

private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
			DefaultBeanDefinitionDocumentReader.class;
	// 注册 BeanDefinition		
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
	
	protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
		return BeanUtils.instantiateClass(this.documentReaderClass);
	}

这里 XmlBeanDefinitionReader 创建了一个 BeanDefinitionDocumentReader,其实现为 DefaultBeanDefinitionDocumentReader ,并将 BeanDefinition 的解析注册委托为这个对象。跟踪 DefaultBeanDefinitionDocumentReader#registerBeanDefinitions 源码,其调用方法如下。

protected void doRegisterBeanDefinitions(Element root) {

		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		if (this.delegate.isDefaultNamespace(root)) {
			// 如果 <beans/> 标签使用 Spring 默认的命名空间,并且指定的 profile 未被激活,则不处理
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					
					return;
				}
			}
		}

		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}

这里已经进入了较为核心的处理逻辑,先获取 <beans/> 标签的 profile 属性,如果未被激活则不会再处理,否则开始解析 <beans/> 标签。后面的逻辑已经比较重要,在看的朋友留意。跟踪 parseBeanDefinitions 方法。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						// 默认命名空间直接进行解析
						parseDefaultElement(ele, delegate);
					} else {
						// 自定义命名空间
						delegate.parseCustomElement(ele);
					}
				}
			}
		} else {
			delegate.parseCustomElement(root);
		}
	}

这里对<beans/> 的子标签进行解析,如果发现使用的是默认的命名空间则直接解析,否则解析自定义的命名空间。<beans/> 标签下默认命名空间的标签包括<import/><alias/><bean/><beans/>,按照 xml dtd 或 xsd 的定义中规中矩的解析即可,而非默认命名空间的解析则是 Spring 留给我们扩展使用。下面把重点放到自定义标签的解析,跟踪BeanDefinitionParserDelegate#parseCustomElement(Element)方法,其又会调用如下的方法。

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		// 根据命名空间查询对应的处理器,然后进行处理
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

该方法根据命名空间找到相应的命名空间处理器 NamespaceHandler ,然后进行解析。先看 NamespaceHandler 的定义。

public interface NamespaceHandler {
	// 初始化
	void init();
	// 将标签解析为 BeanDefinition
	@Nullable
	BeanDefinition parse(Element element, ParserContext parserContext);
	// 对 BeanDefinition 再次处理,Spring 使用其解析默认命名空间下标签的非默认命名空间属性或子标签
	@Nullable
	BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);

那么命名空间处理器是如何获取的呢?其实现源码如下。

public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {

	@Override
	@Nullable
	public NamespaceHandler resolve(String namespaceUri) {
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				}
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				namespaceHandler.init();
				// 缓存 NamespaceHandler
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			catch (ClassNotFoundException ex) {
				throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
						"] for namespace [" + namespaceUri + "]", ex);
			}
			catch (LinkageError err) {
				throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
						className + "] for namespace [" + namespaceUri + "]", err);
			}
		}
	}

}

NamespaceHandler 是从 handlerMappings 获取的,获取到后会先调用其 init 方法进行初始化。再来看 handlerMappings 的获取方法。

public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
	public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

	public DefaultNamespaceHandlerResolver() {
		this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
	}
	public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
		this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
	}
	
	private Map<String, Object> getHandlerMappings() {
		Map<String, Object> handlerMappings = this.handlerMappings;
		if (handlerMappings == null) {
			synchronized (this) {
				handlerMappings = this.handlerMappings;
				if (handlerMappings == null) {				
					try {
						Properties mappings =
								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);				
						handlerMappings = new ConcurrentHashMap<>(mappings.size());
						CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
						this.handlerMappings = handlerMappings;
					}
					catch (IOException ex) {
						throw new IllegalStateException(
								"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
					}
				}
			}
		}
		return handlerMappings;
	}
}

这里看到,NamespaceHandler 最终是定义在 META-INF/spring.handlers 位置中,spring-beans 模块中也定义了这样的文件,用于解析命名空间cputil 。查看如下。

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

至此,Spring 默认命名空间和非默认命名空间的 xml 标签,Spring 都完美进行了解析。

自定义 xml 配置解析

根据上面的描述,如果需要解析 Spring xml 配置文件自定义的标签,我们只需要在类路径下 META-INF/spring.handlers 文件中定义自己的 NamespaceHandler 即可,事实上 Spring 提供了一个便于我们实现自己业务逻辑的 NamespaceHandlerSupport。代码较少,这里直接拿来。

public abstract class NamespaceHandlerSupport implements NamespaceHandler {
	private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
	private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();
	private final Map<String, BeanDefinitionDecorator> attributeDecorators = new HashMap<>();
	
	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		return (parser != null ? parser.parse(element, parserContext) : null);
	}

	@Nullable
	private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
		String localName = parserContext.getDelegate().getLocalName(element);
		BeanDefinitionParser parser = this.parsers.get(localName);
		if (parser == null) {
			parserContext.getReaderContext().fatal(
					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
		}
		return parser;
	}

	@Override
	@Nullable
	public BeanDefinitionHolder decorate(
			Node node, BeanDefinitionHolder definition, ParserContext parserContext) {

		BeanDefinitionDecorator decorator = findDecoratorForNode(node, parserContext);
		return (decorator != null ? decorator.decorate(node, definition, parserContext) : null);
	}

	@Nullable
	private BeanDefinitionDecorator findDecoratorForNode(Node node, ParserContext parserContext) {
		BeanDefinitionDecorator decorator = null;
		String localName = parserContext.getDelegate().getLocalName(node);
		if (node instanceof Element) {
			decorator = this.decorators.get(localName);
		}
		else if (node instanceof Attr) {
			decorator = this.attributeDecorators.get(localName);
		}
		else {
			parserContext.getReaderContext().fatal(
					"Cannot decorate based on Nodes of type [" + node.getClass().getName() + "]", node);
		}
		if (decorator == null) {
			parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionDecorator for " +
					(node instanceof Element ? "element" : "attribute") + " [" + localName + "]", node);
		}
		return decorator;
	}

	protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
		this.parsers.put(elementName, parser);
	}

	protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) {
		this.decorators.put(elementName, dec);
	}

	protected final void registerBeanDefinitionDecoratorForAttribute(String attrName, BeanDefinitionDecorator dec) {
		this.attributeDecorators.put(attrName, dec);
	}

}

NamespaceHandlerSupport 将标签、属性、子标签的解析分别委托给 BeanDefinitionParser、BeanDefinitionDecorator。我们在 init 方法进行注册即可。

至此,总结 Spring 自定义 xml 配置文件标签解析的流程如下。

  1. 定义自己的命名空间对应的 xsd 文件,并在 META-INF/spring.schemas 文件注册,便于直接查找本地文件。
  2. 在 Spring xml 配置文件中指定自己的命名空间及对应的 xsd 文件路径。
  3. 实现 NamespaceHandlerSupport 类,在 init 方法注册自己的 BeanDefinitionParser、BeanDefinitionDecorator。
  4. 在类路径文件 META-INF/spring.handlers 中定义自己的 NamespaceHandler 。

BeanDefinition 注册

BeanDefinition 由 BeanDefinitionRegistry 进行注册。BeanDefinitionRegistry 定义如下。

public interface BeanDefinitionRegistry extends AliasRegistry {
	// 注册 BeanDefinition
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;
			
	// 移除注册的 BeanDefinition
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	// 获取注册的 BeanDefinition
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	// 是否注册了给定名称的 BeanDefinition
	boolean containsBeanDefinition(String beanName);

	// 获取已经注册的 BeanDefinition
	String[] getBeanDefinitionNames();

	// 获取注册的 BeanDefinition 的数量
	int getBeanDefinitionCount();

	// 给定名称的 BeanDefinition 是否已经注册,包括别名
	boolean isBeanNameInUse(String beanName);

}

BeanDefinitionRegistry 由 DefaultListableBeanFactory 进行实现,内部使用 map 保存 BeanDefinition,ApplicationContext 应用上下文的实现类中也实现了 BeanDefinitionRegistry ,其底层会委托给 DefaultListableBeanFactory 。如果我们需要注册自定义的 BeanDefinition,只需要通过 BeanFactoryAware 、@Autowire 注入、ConfigurableApplicationContext 等方法获取 DefaultListableBeanFactory 实例即可。

总结

BeanDefinition 作为 bean 定义的元数据,是理解 Spring 的基石,本篇对 BeanDefinition 的分类、解析、注册详细进行了描述,希望对大家有帮忙,欢迎留言讨论。