您的位置:首页 > 编程语言 > Java开发

Spring bean定义解析源码分析

2015-02-24 21:13 621 查看
在上一篇Spring IOC容器启动简介中在ClassPathXmlApplicationContext的基础粗略的分析了IOC容器的启动过程,对一些比较复杂的步骤没有详细的说明,从本篇开始对其中的一些比较复杂的步骤进行分析。本篇对基于ClassPathXmlApplicationContext的IOC容器的bean定义的解析与加载过程进行分析。bean定义解析加载的简单时序图如下:



bean定义的解析通过XmlBeanDefinitionReader来完成,在解析前先做一些准备工作:1、设置环境变量(Environment)用来匹配在bean配置文件可能出现的一些占位符;2、设置资源定位器用来定位bean的xml定义文件;3、设置xml实体处理器辅助xml的解析工作。

XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
XmlBeanDefinitionReader通过DocumentLoader把bean的定义文件解析成一颗DOM树,然后对DOM中的各个结点就行解析。

XmlBeanDefinitionReader的doLoadBeanDefinitions方法

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
接下来是对DOM树根节点中的各个结点进行解析:

首先解析检查是否存在profile属性,如果定义了profile属性,判断该profile和设置到坏境中的profile是否匹配,如果不匹配说明这颗子树定义的bean不适用于当前的运行坏境,跳过当前整个颗子树的解析,见DefaultBeanDefinitionDocumentReader类的doRegisterBeanDefinitions方法中的代码片段

String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
return;
}
}
循环各个子树,检查节点是默认的名称空间还是扩展的名称空间,比如<bean>、<property>是默认的名称空间,而<aop:xxx>、<context:xxx>这种标签是扩展的标签需要额外的标签处理器,默认标签的一级标签是<beans>,先看一下默认名称空间。

默认名称空间有几个二级标签import、alias、bean、beans。

import标签,读取resource属性,如果未设置解析报错,判断resource对应的路径是绝对路径还是相对路径,如果是相对路径需要把路径装换成完整的路径,然后递归调用loadBeanDefinitions

beans标签,直接递归调用doRegisterBeanDefinitions

alias标签,读取name和alias属性,把该别名映射注册到容器中

代码在DefaultBeanDefinitionDocumentReader的processAliasRegistration方法

protected void processAliasRegistration(Element ele) {
String name = ele.getAttribute(NAME_ATTRIBUTE);
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
if (valid) {
try {
getReaderContext().getRegistry().registerAlias(name, alias);
}
catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
bean标签,解析bean标签下的子树是整个默认名称空间标签解析最核心的动作,import和beans标签最后也会走到这一步。bean标签的解析逻辑被委托给BeanDefinitionParserDelegate:

首先读取bean的id属性和name属性,分别代码bean的id和别名,name如果以逗号或分号分隔,分裂成别名数组。如果id没有设置,取第一个别名当做id,并且把这个别名从别名数组中删除。

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}

String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}

...

}
如果bean不是内部bean,检查id和别名是否已经被其它的bean使用,如果已经被占用,解析报错。否则把id和别名都放到一个usedNames集合属性中,声明这些id和别名已经被占用。之所以内部bean不用检查时因为内部bean的id是容器生成的,容器会保证不会发生重复。parseBeanDefinitionElement等方法中有一个containingBean参数,这个参数用来区分当前解析的bean是否是内部bean,如果是非空值说明是内部bean,内部bean和非内部bean解析时只做了两件不同的事情:1、id生成策略;2、内部bean需要继承其外部bean的scope。

protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {
String foundName = null;

if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {
foundName = beanName;
}
if (foundName == null) {
foundName = (String) CollectionUtils.findFirstMatch(this.usedNames, aliases);
}
if (foundName != null) {
error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);
}

this.usedNames.add(beanName);
this.usedNames.addAll(aliases);
}


如果既没有设置id也没有设置别名,那么容器会给bean自动生成一个id,生成规则如下:

1、在定义bean时是否指定了class,如果指定了取类名,如果未指定,判断是否指定了parent,如果指定了取parent名+$child,如果未指定判断是否指定了factory bean,如果指定了取factory bean名+$created,如果都未指定说明配置有错误

2、如果是内部bean,取步骤一生成的名称+#+随机数

3、如果不是内部bean,如果步骤一生成的名称在容器中不存在,取步骤一生成的名称,否则取步骤一生成的名称+#计数,如果计数已经被使用再次计数直到计数未被使用为止

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {

...

if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}

...
}

return null;
}

public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {

String generatedBeanName = definition.getBeanClassName();
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
}

String id = generatedBeanName;
if (isInnerBean) {
// Inner bean: generate identity hashcode suffix.
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}
else {
// Top-level bean: use plain class name.
// Increase counter until the id is unique.
int counter = -1;
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
}
}
return id;
}


接下来解析其它属性和子标签比如class、parent、scope、lazy-init、lookup-method等,生成一个GenericBeanDefinition对象,并且把这些属性封装到GenericBeanDefinition对象相应的属性中,代码在BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法:

public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {

this.parseState.push(new BeanEntry(beanName));

String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}

try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
AbstractBeanDefinition bd = createBeanDefinition(className, parent);

parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);

bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));

return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}

return null;
}
最后如果定义了修饰器BeanDefinitionDecorator,对bean定义进行修改修饰,AOP标签定义了修饰器,这个在后面的文章介绍,然后把解析出来的bean定义和别名都注册到容器中。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {

// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}


以上是默认名称空间的标签解析,如果名称DOM子树根节点的名称空间是扩展的,那么进入BeanDefinitionParserDelegate的parseCustomElement方法,大致时序图如下:



首先需要查找一个名称空间处理器,代码如下:

public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

public DefaultNamespaceHandlerResolver() {
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}

private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
//handlerMappingsLocation=DEFAULT_HANDLER_MAPPINGS_LOCATION
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(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 this.handlerMappings;
}


名称空间和名称空间处理器的映射关系定义在jar包的META-INF/spring.handlers文件中,比如spring-context-3.2.9.RELEASE.jar的spring.handlers的内容如下:

http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
我们可以看到context的名称空间对应的处理是ContextNamespaceHandler,jee名称空间的处理器是JeeNamespaceHandler等。框架把所有Spring相关jar包中的spring.handlers文件加载出来并且存储名称空间和处理器的映射关系。找到处理器之后调用处理器的parse方法。

public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}

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;
}
从上面代码可以看出首先定位出待解析的标签对应的解析器,以ContextNamespaceHandler为例,从ContextNamespaceHandler的代码可以看出context名称空间下所有标签的解析器。

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}

}
找到解析器之后再调用解析器的parse方法解析具体的标签,比如annotation-config标签的解析器是AnnotationConfigBeanDefinitionParser,代码如下,在解析时遇到annotation-config标签时会注册ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor等BPP到容器中。

public class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser {

public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);

// Obtain bean definitions for all relevant BeanPostProcessors.
Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);

// Register component for the surrounding <context:annotation-config> element.
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
parserContext.pushContainingComponent(compDefinition);

// Nest the concrete beans in the surrounding component.
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));
}

// Finally register the composite component.
parserContext.popAndRegisterContainingComponent();

return null;
}

}
关于扩展名称空间下的标签就不一个个分析,知道如何找相关的代码就好。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: