diff --git a/docs/Spring/clazz/Spring-BeanDefinitionParserDelegate.md b/docs/Spring/clazz/Spring-BeanDefinitionParserDelegate.md new file mode 100644 index 0000000..d31d8a3 --- /dev/null +++ b/docs/Spring/clazz/Spring-BeanDefinitionParserDelegate.md @@ -0,0 +1,905 @@ +# Spring-BeanDefinitionParserDelegate +- Author: [HuiFer](https://github.com/huifer) +- 源码路径: `org.springframework.beans.factory.xml.BeanDefinitionParserDelegate` +- 注意: 贴出的代码为当前类(`BeanDefinitionParserDelegate`)没有将其他的调用代码贴出,详细请看[huifer-srping](https://github.com/huifer/spring-framework). +- 该类的作用是将标签解析 + +- 下面这段代码是这个类的入口 + +```java + @Nullable + public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { + // 获取id 属性 + String id = ele.getAttribute(ID_ATTRIBUTE); + // 获取 name 属性 + String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); + + // 别名列表 + List aliases = new ArrayList<>(); + if (StringUtils.hasLength(nameAttr)) { + String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); + aliases.addAll(Arrays.asList(nameArr)); + } + + // beanName = 中id的属性值 + 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; + } +``` + +![image-20191231142829639](/image/spring/image-20191231142829639.png) + +该类大量的`parseXXX`开头的代码,这些方法都是对标签进行解析转换成具体的实体 + + + +### 测试用例 + +```xml + + + + + +``` + + + +## parseBeanDefinitionAttributes + +- 解析属性`scope`,`abstract`,`lazy-init` + +```java +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; + } +``` + + +![image-20191231162505748](/image/spring/image-20191231162505748.png) + +其他的标签也同样的方式**`getAttribute`**获取 + +思路: + +1. 判断是否有这个标签 + 1. 有直接获取,设置值 + 2. 没有跳过 + + + + +## parseMetaElements + +```java + /** + * 解析Meta 元素 + * {@code } => {@link BeanMetadataAttribute} + * Parse the meta elements underneath the given element, if any. + */ + 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; + // 获取 key 属性值 + String key = metaElement.getAttribute(KEY_ATTRIBUTE); + // 获取 value 属性值 + String value = metaElement.getAttribute(VALUE_ATTRIBUTE); + BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value); + attribute.setSource(extractSource(metaElement)); + attributeAccessor.addMetadataAttribute(attribute); + } + } + } + +``` + +![image-20191231164622063](/image/spring/image-20191231164622063.png) + + + + + +## parseLookupOverrideSubElements + +```java + /** + * 解析{@code } + * 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); + // 转换成JAVA对象 + LookupOverride override = new LookupOverride(methodName, beanRef); + override.setSource(extractSource(ele)); + overrides.addOverride(override); + } + } + } + +``` + +### 测试用例 + +```xml + + + + + + + + +``` + +![image-20191231165638975](/image/spring/image-20191231165638975.png) + + + + + + + +## parseReplacedMethodSubElements + +```java + /** + * {@code } + * 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); + // 转换成JAVA对象 + ReplaceOverride replaceOverride = new ReplaceOverride(name, callback); + // Look for arg-type match elements. + // 参数解析 解析 + List 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); + } + } + } + +``` + +### 测试用例 + + 编写方法`dis` + +```java +public class Person { + private String name; + private Apple apple; + private Integer age; + + public void dis() { + System.out.println("dis"); + } +} +``` + +​ 编写一个替换`dis`方法的类 + +```java +public class Rc implements MethodReplacer { + @Override + public Object reimplement(Object obj, Method method, Object[] args) throws Throwable { + System.out.println("替换原来的方法"); + return null; + } +} +``` + +xml 配置 + +```java + + + +``` + +![image-20200101093742238](/image/spring/image-20200101093742238.png) + + + + + +## parseConstructorArgElements + +```java + 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); + } + } + } + +``` + +```java + /** + * 解析 constructor-arg 元素 + * {@code } + * Parse a constructor-arg element. + */ + public void parseConstructorArgElement(Element ele, BeanDefinition bd) { + 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 { + // ConstructorArgumentEntry 插入parseState + this.parseState.push(new ConstructorArgumentEntry(index)); + Object value = parsePropertyValue(ele, bd, null); + // 转换成JAVA对象 + ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); + if (StringUtils.hasLength(typeAttr)) { + valueHolder.setType(typeAttr); + } + 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 { + try { + this.parseState.push(new ConstructorArgumentEntry()); + Object value = parsePropertyValue(ele, bd, null); + ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); + if (StringUtils.hasLength(typeAttr)) { + valueHolder.setType(typeAttr); + } + if (StringUtils.hasLength(nameAttr)) { + valueHolder.setName(nameAttr); + } + valueHolder.setSource(extractSource(ele)); + bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); + } + finally { + this.parseState.pop(); + } + } + } + +``` + +```JAVA + /** + * Get the value of a property element. May be a list etc. + * Also used for constructor arguments, "propertyName" being null in this case. + */ + @Nullable + public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) { + String elementName = (propertyName != null ? + " element for property '" + propertyName + "'" : + " element"); + + // Should only have one child element: ref, value, list, etc. + NodeList nl = ele.getChildNodes(); + Element subElement = null; + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + // 不处理 element 节点名称 = description 和 meta + if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && + !nodeNameEquals(node, META_ELEMENT)) { + // Child element is what we're looking for. + if (subElement != null) { + error(elementName + " must not contain more than one sub-element", ele); + } + else { + subElement = (Element) node; + } + } + } + + // 判断是否存在 ref 属性 + boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); + // 判断是否 value 属性 + boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); + // 判断1: ref属性存在 value 属性 或者 ref 和 value 有一个存在并且有 下级节点 抛出异常 + if ((hasRefAttribute && hasValueAttribute) || + ((hasRefAttribute || hasValueAttribute) && subElement != null)) { + error(elementName + + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); + } + + // 判断2: 存在 ref 返回 ref 的值 + if (hasRefAttribute) { + String refName = ele.getAttribute(REF_ATTRIBUTE); + if (!StringUtils.hasText(refName)) { + error(elementName + " contains empty 'ref' attribute", ele); + } + RuntimeBeanReference ref = new RuntimeBeanReference(refName); + ref.setSource(extractSource(ele)); + return ref; + } + // 判断3: 存在 value 返回 value 的值 + else if (hasValueAttribute) { + TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); + valueHolder.setSource(extractSource(ele)); + return valueHolder; + } + else if (subElement != null) { + // 下级标签解析 + return parsePropertySubElement(subElement, bd); + } + else { + // 没有抛出异常 + // Neither child element nor "ref" or "value" attribute found. + error(elementName + " must specify a ref or value", ele); + return null; + } + } + +``` + + + +### 测试用例 + +- 编辑构造函数 + + ```java + public class Person { + private String name; + private Apple apple; + private Integer age; + + public Person() { + } + + public Person(Integer age) { + this.age = age; + } + + ``` + +- 添加配置 + + ```xml + + + ``` + + + +![image-20200101100906778](/image/spring/image-20200101100906778.png) + + + +## parsePropertyElements + +```java +public void parsePropertyElements(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, PROPERTY_ELEMENT)) { + parsePropertyElement((Element) node, bd); + } + } + } +``` + +```java + /** + * 解析 {@code } + * Parse a property element. + */ + public void parsePropertyElement(Element ele, BeanDefinition bd) { + String propertyName = ele.getAttribute(NAME_ATTRIBUTE); + if (!StringUtils.hasLength(propertyName)) { + error("Tag 'property' must have a 'name' attribute", ele); + return; + } + this.parseState.push(new PropertyEntry(propertyName)); + try { + if (bd.getPropertyValues().contains(propertyName)) { + error("Multiple 'property' definitions for property '" + propertyName + "'", ele); + return; + } + Object val = parsePropertyValue(ele, bd, propertyName); + // 转换JAVA 对象 + PropertyValue pv = new PropertyValue(propertyName, val); + parseMetaElements(ele, pv); + pv.setSource(extractSource(ele)); + bd.getPropertyValues().addPropertyValue(pv); + } + finally { + this.parseState.pop(); + } + } + +``` + +### 测试用例 + +xml + +```xml + + + +``` + + + +![image-20200101111755022](/image/spring/image-20200101111755022.png) + + + +## parseQualifierElements + +```java +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); + } + } + } +``` + +```java + /** + * 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; + } + // 创建并且放入 parseState + 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; + // 获取 下级biao'qia + String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE); + String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE); + // 判断是否有 key 和 value + if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) { + // 转换成JAVA对象 + 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(); + } + } + +``` + + + +### 测试用例 + +- 编写一个注解 + +```java +/** + * 在spring-config.xml中配置 ,使用时通过 status + quality 来引入具体的bean + */ +@Target({ElementType.FIELD, ElementType.METHOD, + ElementType.TYPE, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Qualifier +public @interface PersonQualifier { + + String status(); + + String quality(); +} +``` + +```JAVA +public class PersonS { + private String personName; + + public String getPersonName() { + return personName; + } + + public void setPersonName(String personName) { + this.personName = personName; + } +} +``` + +```JAVA + +import org.springframework.beans.factory.annotation.Autowired; + +public class PersonService { + @Autowired +// @PersonQualifier(status = "status_teacher", quality = "quality_teacher") + @PersonQualifier(status = "status_student", quality = "quality_student") + private PersonS personS; + + public PersonS getPerson() { + return personS; + } + + public void setPerson(PersonS person) { + this.personS = person; + } +} +``` + +```JAVA +public class Student extends PersonS { + private String stdLocation; + + public String getStdLocation() { + return stdLocation; + } + + public void setStdLocation(String stdLocation) { + this.stdLocation = stdLocation; + } +} +``` + +```JAVA +public class Teacher extends PersonS { + private String subject; + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } +} +``` + +```JAVA +public class QualifierSourceCode { + public static void main(String[] args) { + AbstractApplicationContext context = new ClassPathXmlApplicationContext("QualifierSourceCode-beans.xml"); + PersonService service = context.getBean(PersonService.class); + System.out.println(service.getPerson().getPersonName()); + context.close(); + } +} + +``` + +```XML + + + + + + + + + + + + + + + + + + + + + + + +``` + +![image-20200101155539501](/image/spring/image-20200101155539501.png) + + + + + +### Bean解析结果 + +![image-20200102083512005](/image/spring/image-20200102083512005.png) + + + + + +## importBeanDefinitionResource + +```JAVA + /** + * 解析{@code } 标签 + * Parse an "import" element and load the bean definitions + * from the given resource into the bean factory. + */ + protected void importBeanDefinitionResource(Element ele) { + // 获取 resource 属性 + String location = ele.getAttribute(RESOURCE_ATTRIBUTE); + // 是否有 resource 属性 + if (!StringUtils.hasText(location)) { + getReaderContext().error("Resource location must not be empty", ele); + return; + } + + // Resolve system properties: e.g. "${user.dir}" + // 获取系统环境,解析路径 + /** + * 1. getReaderContext() 获取{@link XmlReaderContext} + * 2. getEnvironment() 获取环境 + * 3. resolveRequiredPlaceholders(location) 解析占位符${...} + */ + location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); + + // 资源存放集合 + Set 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 { + // 获取import中的数量 + 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()) { + // 此处调用方式和加载一个xml文件相同 + 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]); + /*** + * fireImportProcessed()触发import事件, + * 并且通过{@link org.springframework.beans.factory.parsing.ReaderEventListener#importProcessed(ImportDefinition)} 宣布处理结果 + */ + getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); + } + +``` + + + +![image-20200102085031641](/image/spring/image-20200102085031641.png) + +![image-20200102091421516](/image/spring/image-20200102091421516.png) + + +