赞
踩
BeanDefinition 直译为 bean 定义,描述了一个 bean 实例具有的构造方法参数和属性值等信息。与 Java 中的 Class 类似,Class 是类文件在内存中的表现形式,BeanDefinition 是 Spring Bean 配置元信息在内存中的表现形式,各种配置元信息最后都会被转换为 BeanDefinition ,Spring 根据 BeanDefinition 实例化、初始化 bean,BeanDefinition 涉及到 Spring Bean 的整个生命周期。
在 Spring 3 之前,我们最常用 xml 配置 Spring 中的 bean,Spring 3 及之后,注解逐渐成为使用 Spring 的主要方式。此外较少使用的 bean 配置元数据还包括 properties、groovy,我们甚至可以定义自己的 BeanDefinition。Spring 中的 BeanDefinition 主要如下图所示。
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,包含类或方法的注解元信息。
通常情况下,Spring 在解析不同的配置元信息时会使用不同的 BeanDefinition,我们不必进行关心。而如果我们想通过编程式向 Spring 注册 BeanDefinition ,我们则需要掌握创建 BeanDefinition 的一些技巧。具有两种创建 BeanDefinition 的方式。
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(String.class)
.setLazyInit(true).setAbstract(false).getBeanDefinition();
BeanDefinition 通过 BeanDefinitionReader 解析配置元数据获取,BeanDefinitionReader 根据不同的配置元数据具有不同的实现,具体如下。
根据如上类图,可以发现,除了 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 配置文件的解析由 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 模块中也定义了这样的文件,用于解析命名空间c
、p
、util
。查看如下。
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 都完美进行了解析。
根据上面的描述,如果需要解析 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 配置文件标签解析的流程如下。
META-INF/spring.schemas
文件注册,便于直接查找本地文件。META-INF/spring.handlers
中定义自己的 NamespaceHandler 。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 的分类、解析、注册详细进行了描述,希望对大家有帮忙,欢迎留言讨论。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。