上篇說到spring提供了方法可以在實際解析bean定義方法parseBeanDefinitions前後加自定義的操作,本篇我們詳述一下parseBeanDefinitions內部實現。
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
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);
}
}
代碼很簡單,遍歷判斷每個元素是自定義的還是spring本身的,分別走不同的處理邏輯。其中,處理默認元素的方法如下:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
/**
* Determine whether the name of the supplied node is equal to the supplied name.
* <p>The default implementation checks the supplied desired name against both
* {@link Node#getNodeName()} and {@link Node#getLocalName()}.
* <p>Subclasses may override the default implementation to provide a different
* mechanism for comparing node names.
*
* 大意是判斷給定的node節點的名字是否等於給定的名字,默認的實現
* 會根據node的getname或者getlocalname 和給定的名字做判斷
* 當然,你可以自定義子類重寫這個方法實現不同的比較名字的機制
*
* @param node the node to compare
* @param desiredName the name to check for
*/
public boolean nodeNameEquals(Node node, String desiredName) {
return desiredName.equals(node.getNodeName()) || desiredName.equals(getLocalName(node));
下面就是根據不同的判斷條件走不同的解析方法,
/**
* Parse an "import" element and load the bean
* definitions from the given resource into the
* bean factory.
* 解析import元素並從給定的資源中加載bean定義到bean工廠
*/
protected void importBeanDefinitionResource(Element ele) {
//獲取元素的resource屬性,如果獲取不到,直接返回
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// Resolve system properties: e.g. "${user.dir}"
//*獲取到的resource可能是配置的變量,通過下面的方法可以解析獲取對應環境下面的真實值*這個後面單開博客
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
// Discover whether the location is an absolute or relative URI
//判斷獲取的路徑是絕對路徑還是相對路徑
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
// cannot convert to an URI, considering the location relative
// unless it is the well-known Spring prefix "classpath*:"
}
// Absolute or relative?
if (absoluteLocation) {
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isTraceEnabled()) {
logger.trace("Imported " + importCount + " bean definitions from URL location [" + location + "]");
}
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
}
else {
// No URL -> considering resource location as relative to the current file.
try {
int importCount;
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
else {
String baseLocation = getReaderContext().getResource().getURL().toString();
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isTraceEnabled()) {
logger.trace("Imported " + importCount + " bean definitions from relative location [" + location + "]");
}
}
catch (IOException ex) {
getReaderContext().error("Failed to resolve current resource location", ele, ex);
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from relative location [" + location + "]", ele, ex);
}
}
Resource[] actResArray = actualResources.toArray(new Resource[0]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
可以看到,程序運行分爲兩個支路,如果是絕對路徑,那麼調用代碼獲取導入個數
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
//實際調用的是抽象類AbstractBeanDefinitionReader實現的方法loadBeanDefinitions
/**
* Load bean definitions from the specified resource location.
* <p>The location can also be a location pattern, provided that the
* ResourceLoader of this bean definition reader is a ResourcePatternResolver.
* @param location the resource location, to be loaded with the ResourceLoader
* (or ResourcePatternResolver) of this bean definition reader
* @param actualResources a Set to be filled with the actual Resource objects
* that have been resolved during the loading process. May be {@code null}
* to indicate that the caller is not interested in those Resource objects.
*
* 方法註釋大意是:從給定的資源路徑上加載bean定義,這個位置也可
* 以是一種位置模式。只要這個bean定義讀取器的resourceloader是
* 一個ResourcePatternResolver
*
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #getResourceLoader()
* @see #loadBeanDefinitions(org.springframework.core.io.Resource)
* @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
*/
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
if和else內部操作類似,都是調用getresource方法獲取資源,然後回到調用xmlbeanDefinitionReader的loadBeanDefinitions的方法加載bean定義,最後返回加載的bean數量,這個回頭看也好理解,當我們配置一個import元素的時候,意思就是需要spring讀取對應路徑下的配置文件並把文件裏面的bean定義加載到工廠中。而如果import導入的xml裏面還有import元素的話,繼續遞歸處理。至此import元素處理加載完畢。
下一個方法是針對別名的處理:
/**
* Process the given alias element, registering the alias with the registry.
*/
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));
}
}
代碼很簡單,既然設置別名,肯定名字和別名都不能爲空,然後調用registerAlias方法註冊別名
public class SimpleAliasRegistry implements AliasRegistry {
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isDebugEnabled()) {
logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
}
在往別名-規範名字的map放之前有個操作很有意思,spring會先校驗這個map有沒有循環的別名,即A->B,B->C,C->A,這種,如果有的話拋出異常,具體代碼實現如下:
/**
* Check whether the given name points back to the given alias as an alias
* in the other direction already, catching a circular reference upfront
* and throwing a corresponding IllegalStateException.
* @param name the candidate name
* @param alias the candidate alias
* @see #registerAlias
* @see #hasAlias
*/
protected void checkForAliasCircle(String name, String alias) {
if (hasAlias(alias, name)) {
throw new IllegalStateException("Cannot register alias '" + alias +
"' for name '" + name + "': Circular reference - '" +
name + "' is a direct or indirect alias for '" + alias + "' already");
}
}
/**
* Determine whether the given name has the given alias registered.
* @param name the name to check
* @param alias the alias to look for
* @since 4.2.1
*/
public boolean hasAlias(String name, String alias) {
for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
String registeredName = entry.getValue();
if (registeredName.equals(name)) {
String registeredAlias = entry.getKey();
if (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias)) {
return true;
}
}
}
return false;
}
這邊代碼處理用了遞歸,很有意思,我饒了好半天,有興趣可以仔細研究。,如果別名定義沒問題的話,最後會放到別名map中。
然後調用getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
方法,給監聽器註冊事件,默認實現是空
public class EmptyReaderEventListener implements ReaderEventListener {
/**
* Fire an alias-registered event.
*/
public void fireAliasRegistered(String beanName, String alias, @Nullable Object source) {
this.eventListener.aliasRegistered(new AliasDefinition(beanName, alias, source));
}
}
第三個方法是解析bean元素,這是核心中的核心
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
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));
}
}
第一行,創建了beanDefinitionHolder
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
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.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, 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.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
1、獲取bean標籤裏面定義的id和name屬性
2、如果name屬性不爲空,用規定的字符“,;”切割成字符串數組保存到aliases裏面
3、如果沒有定義bean的id屬性,但是定義了bean的name 屬性,那麼取切割後的alias數組的第一個元素作爲bean的名字,並刪除alias第一個元素,(不刪除的話bean的名字和別名相同會出問題)。
4、如果傳入參數containingBean爲null,需要校驗名字唯一性,校驗方法如下,如果bean名字或者bean的別名出現在bean代理已經使用過的名字,那麼拋出異常,說明這個名字已經被使用,沒有的話,將bean的名字和別名加入已經使用的名字Set集合usedNames
/**
* Validate that the specified bean name and aliases have not been used already
* within the current level of beans element nesting.
*/
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 = 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);
}
5、創建BeanDefinition,解析bean定義本身,不考慮名字和別名,如果解析過程出現異常可能返回null,源碼如下:
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* {@code null} if problems occurred during the parsing of the bean definition.
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
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;
}
5.1 先根據bean名字創建一個BeanEntry,並放到成員變量parseState中,獲取class和parent屬性,然後根據這兩個屬性創建AbstractBeanDefinition,
/**
* Create a bean definition for the given class name and parent name.
* @param className the name of the bean class
* @param parentName the name of the bean's parent bean
* @return the newly created bean definition
* @throws ClassNotFoundException if bean class resolution was attempted but failed
*/
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
/**
* Create a new GenericBeanDefinition for the given parent name and class name,
* eagerly loading the bean class if a ClassLoader has been specified.
* @param parentName the name of the parent bean, if any
* @param className the name of the bean class, if any
* @param classLoader the ClassLoader to use for loading bean classes
* (can be {@code null} to just register bean classes by name)
* @return the bean definition
* @throws ClassNotFoundException if the bean class could not be loaded
*/
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
5.2 解析bean定義的屬性
/**
* Apply the attributes of the given bean element to the given bean * definition.
* @param ele bean declaration element
* @param beanName bean name
* @param containingBean containing bean definition
* @return a bean definition initialized according to the bean element attributes
*/
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
這裏主要是bean具體一些屬性的設值,就不挨個展開說了,提醒一下有點特殊的幾個,一個是scope的設值,這邊如果是singleton直接拋出異常,lazyinit,init-method之類的沒設置會用上一級beans標籤裏面的設置
5.3 設置bean內部定義的description屬性
5.4 解析子元素,即<meta key=“test” value=“value”>
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//如果本節點沒設置屬性並且父節點有默認值,那麼繼續
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
//創建BeanMetadataAttribute 對象,並保存到attributeAccessor也就是,傳進來的beandedinition中
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
5.5 解析spring容器look-up標籤,主要用於獲取器的注入
/**
* Parse lookup-override sub-elements of the given bean element.
*/
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
String beanRef = ele.getAttribute(BEAN_ELEMENT);
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
5.6 spring標籤replaced-method標籤解析
/**
* Parse replaced-method sub-elements of the given bean element.
*/
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
Element replacedMethodEle = (Element) node;
String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
// Look for arg-type match elements.
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
overrides.addOverride(replaceOverride);
}
}
}
5.7 解析子標籤的構造參數
/**
* Parse constructor-arg sub-elements of the given bean element.
*/
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
parseConstructorArgElement((Element) node, bd);
}
}
}
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 獲取index,type和name屬性值
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 當有index屬性值時的處理方式
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
// 對屬性值進行解析
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new
ConstructorArgumentValues.ValueHolder(value);
// 封裝type屬性值
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
// 封裝name屬性值
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)){
error("Ambiguous constructor-arg entries for index " + index,ele);
}
else {
bd.getConstructorArgumentValues().addIndexedArgumentValue(index,
valueHolder);
}
}
finally {
this.parseState.pop();
}
}
} catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
else { // 當沒有index屬性值時的處理方式
try {
this.parseState.push(new ConstructorArgumentEntry());
// 對屬性值進行解析
Object value = parsePropertyValue(ele, bd, null);
// 對解析的屬性值進行封裝
ConstructorArgumentValues.ValueHolder valueHolder = new
ConstructorArgumentValues.ValueHolder(value);
// 設置type屬性值
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
// 設置name屬性值
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
// 如果該子標籤使用的自定義的標籤名,則使用自定義子標籤進行解析
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
// 如果當前子標籤是bean子標籤,則遞歸的對當前bean進行解析
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// 如果當前子標籤是ref子標籤,則獲取其bean屬性的值,並且將其封裝到RuntimeBeanReference對象中
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
// 如果當前子標籤是idref子標籤,則還是將其封裝到一個RuntimeBeanReference對象中
return parseIdRefElement(ele);
}
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
// 如果當前子標籤是value子標籤,則將其封裝到一個TypedStringValue對象中,
// 並且會檢查其type屬性指定的class是否存在
return parseValueElement(ele, defaultValueType);
}
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// 如果子標籤是一個null子標籤,則封裝一個包含空屬性的TypedStringValue對象
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
// 如果是array子標籤,則對array子標籤進行解析
return parseArrayElement(ele, bd);
}
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
// 如果是list子標籤,則對list子標籤進行解析
return parseListElement(ele, bd);
}
else if (nodeNameEquals(ele, SET_ELEMENT)) {
// 如果是set子標籤,則對set子標籤進行解析
return parseSetElement(ele, bd);
}
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
// 如果是map子標籤,則對map子標籤進行解析
return parseMapElement(ele, bd);
}
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
// 如果是props子標籤,則對props子標籤進行解析
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
5.8 spring的qualifier標籤解析
/**
* Parse qualifier sub-elements of the given bean element.
*/
public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
parseQualifierElement((Element) node, bd);
}
}
}
/**
* Parse a qualifier element.
*/
public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
if (!StringUtils.hasLength(typeName)) {
error("Tag 'qualifier' must have a 'type' attribute", ele);
return;
}
this.parseState.push(new QualifierEntry(typeName));
try {
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
qualifier.setSource(extractSource(ele));
String value = ele.getAttribute(VALUE_ATTRIBUTE);
if (StringUtils.hasLength(value)) {
qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
}
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
Element attributeEle = (Element) node;
String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
attribute.setSource(extractSource(attributeEle));
qualifier.addMetadataAttribute(attribute);
}
else {
error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
return;
}
}
}
bd.addQualifier(qualifier);
}
finally {
this.parseState.pop();
}
}
至此,bean內部一些標籤解析完畢.返回beanDefinition後,接下來會判斷bean有沒有名字,如果沒有名字的話,生成一個bean的名字,這裏面有個操作是判斷當前bean是不是內部bean,然後根據不同的判定結果,生成bean的名字規則不同。最後把beanDefinition,beanname,beanAlias封裝到一個beanDefinitionHolder中。
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.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
創建完返回到上一級DefaultBeanDefinitionDocumentReader,調用delegete的方法如下:
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = definitionHolder;
// Decorate based on custom attributes first.
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
// Decorate based on custom nested elements.
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
這一步主要是會通過你在xml中定義的命名空間,解析配置的一些自定義屬性,這裏多說幾句:
XML命名空間(比如p規則等等,或者自定義的規則)都有以下步驟:
1.編寫XSD文件。
2.編寫NamespaceHandler使用XSD解析XML。
3.編寫spring.handlers和spring.schemas串聯起所有部件。
所以如果不是系統默認命名空間,應該要寫一個繼承了NamespaceHandler的類來使用相應的XSD規則文件解析bean xml。
可以想見,代碼這塊處理的話,肯定是先獲取命名空間判斷是不是默認的,如果不是會根據找對應的自定義標籤的handler類,找不到報錯提示解析失敗,找到的話再利用對應handler的方法解析自定義標籤。
解析完自定義標籤後,所有內容都解析到beandedinition中,然後對需要將這個bean註冊到registry中,即BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
具體源碼實現如下:
/**
* Register the given bean definition with the given bean factory.
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throws BeanDefinitionStoreException if registration failed
*/
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 alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
//---------------------------------------------------------------------
// Implementation of BeanDefinitionRegistry interface
//---------------------------------------------------------------------
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
//註冊前對bean定義做校驗
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//從現有的beandefinitionMap中獲取bean看是否已有bean
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
//如果已存在bean定義,而且不允許用同一個名字重新註冊一個不同的bean,拋出異常
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
//如果已有的bean角色等級比要註冊的等級低
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
//如果兩個bean不是同一個bean
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
//把bean定義放到map中
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
//bean 創建已經開始了,
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
//如果bean已經創建了,並且是以一個單例
resetBeanDefinition(beanName);
}
}
/**
* Reset all bean definition caches for the given bean,
* including the caches of beans that are derived from it.
* <p>Called after an existing bean definition has been replaced or removed,
* triggering {@link #clearMergedBeanDefinition}, {@link #destroySingleton}
* and {@link MergedBeanDefinitionPostProcessor#resetBeanDefinition} on the
* given bean and on all bean definitions that have the given bean as parent.
*
* 重置給定bean的定義緩存,包括派生bean的,就是說這個bean的子
* bean,在一個已經存在的bean被替換或者刪除的時候調用這個方法,
* @param beanName the name of the bean to reset
* @see #registerBeanDefinition
* @see #removeBeanDefinition
*/
protected void resetBeanDefinition(String beanName) {
// Remove the merged bean definition for the given bean, if already created.
clearMergedBeanDefinition(beanName);
// Remove corresponding bean from singleton cache, if any. Shouldn't usually
// be necessary, rather just meant for overriding a context's default beans
// (e.g. the default StaticMessageSource in a StaticApplicationContext).
//從單例緩存中移除相應的bean(如果有的話)。通常不應該是必
//需的,而只是用於重寫上下文的默認bean
destroySingleton(beanName);
// Notify all post-processors that the specified bean definition has been reset.
//通知所有後置處理器,指定的bean定義已經被重置。
for (BeanPostProcessor processor : getBeanPostProcessors()) {
if (processor instanceof MergedBeanDefinitionPostProcessor) {
((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName);
}
}
// Reset all bean definitions that have the given bean as parent (recursively).
//遞歸處理以當前bean爲父bean的重置過程
for (String bdName : this.beanDefinitionNames) {
if (!beanName.equals(bdName)) {
BeanDefinition bd = this.beanDefinitionMap.get(bdName);
if (beanName.equals(bd.getParentName())) {
resetBeanDefinition(bdName);
}
}
}
}
最後,調用getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
方法發送註冊事件。至此bean標籤相關內容解析完畢。
beans標籤的解析,又回到瞭解析bean的方法reader的doRegisterBeanDefinitions,這邊就開始遞歸,知道所有beans標籤都解析完畢。
至此bean定義解析最終完結。這塊確實很繞,第一次看源碼可能一頭霧水,後面畫一個時序圖簡單理一下這塊的調用關係。
最後,推薦一個畫時序圖的工具,PlantUML,比較方便快捷,有興趣可以試試,另外,他作爲一個插件可以和網上大多數流行的開發工具集成,比如eclipse,idea,vccode等
上面時序圖就是我用這個工具畫的,因爲這塊流程還是比較複雜的,嘗試了幾個其他的工具,最後還是覺得這個好用。
因爲時間和精力的原因,這塊處理流程一些細節還是沒有涉及到,後面有機會的話會補充。
感謝閱讀,如有錯誤,請不吝指正