当前位置: 首页 > news >正文

《Spring系列》第2章 解析XML获取Bean

一、基础代码

Spring加载bean实例的代码

public static void main(String[] args) throws IOException {// 1.获取资源Resource resource = new ClassPathResource("bean.xml");// 2.获取BeanFactoryDefaultListableBeanFactory factory = new DefaultListableBeanFactory();// 3.获取资源的解析器XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);// 4.装载资源reader.loadBeanDefinitions(resource);// 5.获取Bean实例Book book = factory.getBean(Book.class);System.out.println(book);
}

这段代码是 Spring 中编程式使用 IoC 容器,通过这四段简单的代码,我们可以初步判断 IoC 容器的使用过程。整个过程就分为三个步骤:

资源定位。我们一般用外部资源来描述 Bean 对象,所以在初始化 IoC 容器的第一步就是需要定位这个外部资源。

装载。装载就是 BeanDefinition 的载入。BeanDefinitionReader 读取、解析 Resource 资源,也就是将用户定义的 Bean 表示成 IoC 容器的内部数据结构:BeanDefinition

注册。向 IoC 容器注册在第二步解析好的 BeanDefinition,这个过程是通过BeanDefinitionRegistry接口来实现的。在 IoC 容器内部其实是将第二个过程解析得到的BeanDefinition注入到一个HashMap容器中,IoC 容器就是通过这个HashMap来维护这些BeanDefinition的。

​ 1)在这里需要注意的一点是这个过程并没有完成依赖注入(Bean 创建),Bean 创建是发生在应用第一次调用getBean()方法,向容器索要 Bean 时。
​ 2)当然我们可以通过设置预处理,即对某个 Bean 设置lazyinit = false 属性,那么这个 Bean 的依赖注入就会在容器初始化的时候完成。

简单点说,上面步骤的结果是:XML Resource -> XML Document -> Bean Definition

二、BeanDefinition

上面讲完了介绍到我们解析的Bean标签会封装成BeanDefinition,那么接下来看一下

1.基础介绍

org.springframework.beans.factory.config.BeanDefinition ,是一个接口,它描述了一个 Bean 实例的定义,包括属性值、构造方法值和继承自它的类的更多信息。代码如下:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {// 其中方法就不粘贴了,太多了
}

2.BeanDefinition 的父关系

BeanDefinition 继承 AttributeAccessorBeanMetadataElement 接口。两个接口定义如下:

org.springframework.cor.AttributeAccessor 接口,定义了与其它对象的(元数据)进行连接和访问的约定,即对属性的修改,包括获取、设置、删除。代码如下:

public interface AttributeAccessor {void setAttribute(String name, @Nullable Object value);@NullableObject getAttribute(String name);@NullableObject removeAttribute(String name);boolean hasAttribute(String name);String[] attributeNames();
}

org.springframework.beans.BeanMetadataElement 接口,Bean 元对象持有的配置元素可以通过 getSource() 方法来获取。代码如下:

public interface BeanMetadataElement {@Nullabledefault Object getSource() {return null;}
}

3.BeanDefinition 的子关系

在这里插入图片描述

我们常用的三个实现类有:

  • org.springframework.beans.factory.support.ChildBeanDefinition
  • org.springframework.beans.factory.support.RootBeanDefinition
  • org.springframework.beans.factory.support.GenericBeanDefinition
  1. ChildBeanDefinitionRootBeanDefinitionGenericBeanDefinition 三者都继承 AbstractBeanDefinition 抽象类,即 AbstractBeanDefinition 对三个子类的共同的类信息进行抽象。
  2. 如果配置文件中定义了父 和 子 ,则父 用 RootBeanDefinition 表示,子 用 ChildBeanDefinition 表示,而没有父 的就使用RootBeanDefinition 表示。
  3. GenericBeanDefinition 为一站式服务类。

三、调用链

在这里插入图片描述

1.loadBeanDefinitions() 加载

加载BeanDefinition

// XmlBeanDefinitionReader XML解析类
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {// 将Resource资源封装成EncodedResource,封装的原因是为了进行编码,保证内容读取的正确性return loadBeanDefinitions(new EncodedResource(resource));
}
// XmlBeanDefinitionReader XML解析类
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {Assert.notNull(encodedResource, "EncodedResource must not be null");if (logger.isTraceEnabled()) {logger.trace("Loading XML bean definitions from " + encodedResource);}// 获取已经加载过的资源Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();// 将当前资源加入记录中,如果已存在,则抛出异常// 主要为了避免一个EncodedResource在加载时,还没加载完成,又加载自身,从而导致死循环if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}// 获取输入流try (InputStream inputStream = encodedResource.getResource().getInputStream()) {InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {// 设置编码inputSource.setEncoding(encodedResource.getEncoding());}// **核心逻辑部分,执行加载 BeanDefinitionreturn doLoadBeanDefinitions(inputSource, encodedResource.getResource());}catch (IOException ex) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);}finally {// 缓存中剔除该资源,代表加载完了该文件currentResources.remove(encodedResource);if (currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}
}

-> doLoadBeanDefinitions()

// XmlBeanDefinitionReader XML解析类
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {// 获取XML Document实例Document doc = doLoadDocument(inputSource, resource);// 根据 Document 实例,注册 Bean 信息int count = registerBeanDefinitions(doc, resource);if (logger.isDebugEnabled()) {logger.debug("Loaded " + count + " bean definitions from " + resource);}return count;}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);}
}

2.doLoadDocument()

XML文件解析成Docuemnt

private DocumentLoader documentLoader = new DefaultDocumentLoader();protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware());
}
// DefaultDocumentLoader 默认的文档加载器
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);if (logger.isTraceEnabled()) {logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");}DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);return builder.parse(inputSource);
}

3.registerBeanDefinitions() 开始注册

注册BeanDefinition

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {// <1>创建对象BeanDefinitionDocumentReaderBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();// <2>获取已注册的BeanDefinition数量int countBefore = getRegistry().getBeanDefinitionCount();// <3>创建XmlReaderContext// <4>注册新的BeandocumentReader.registerBeanDefinitions(doc, createReaderContext(resource));// <5>两个相减,返回注册成功的数量return getRegistry().getBeanDefinitionCount() - countBefore;
}

-> registerBeanDefinitions()

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;doRegisterBeanDefinitions(doc.getDocumentElement());
}

-> doRegisterBeanDefinitions()

该方法特殊处理profile属性,然后进行解析

protected void doRegisterBeanDefinitions(Element root) {// 记录老的 BeanDefinitionParserDelegate 对象BeanDefinitionParserDelegate parent = this.delegate;// 创建 BeanDefinitionParserDelegate 对象,并进行设置到 delegate// *** 它负责解析Document的各种对象this.delegate = createDelegate(getReaderContext(), root, parent);// 检查 <beans /> 根标签的命名空间是否为空,或者是 http://www.springframework.org/schema/beansif (this.delegate.isDefaultNamespace(root)) {// 处理profile属性String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {// 使用分隔符切分,可能有多个 profile 。String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);// 如果所有 profile 都无效,则不进行注册// We cannot use Profiles.of(...) since profile expressions are not supported// in XML config. See SPR-12458 for details.if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isDebugEnabled()) {logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +"] not matching: " + getReaderContext().getResource());}return;}}}// 解析前处理, 空实现preProcessXml(root);// 解析parseBeanDefinitions(root, this.delegate);// 解析后处理, 空实现postProcessXml(root);this.delegate = parent;
}protected void preProcessXml(Element root) {
}protected void postProcessXml(Element root) {
}

4.parseBeanDefinitions() 分类解析

下面代码中会区分2类,

  1. 默认命名空间,例如:<bean id="book" class="com.h3c.ss.Book"></bean>
  2. 自定义注解方式,例如:<tx:annotation-driven>
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {// <1>如果根节点使用默认命名空间,执行默认解析if (delegate.isDefaultNamespace(root)) {// 遍历子节点,也就是XML文件中的内容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 {//<2> 根节点非默认命名空间,执行自定义解析delegate.parseCustomElement(root);}
}

-> parseDefaultElement() 解析默认命名空间

public static final String BEAN_ELEMENT = "bean";
public static final String IMPORT_ELEMENT = "import";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String NESTED_BEANS_ELEMENT = "beans";private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {// 解析import标签if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}// 解析aliaselse if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {processAliasRegistration(ele);}// 解析beanelse if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {processBeanDefinition(ele, delegate);}// 解析嵌套的beanelse if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);}
}

-> parseCustomElement() 自定义解析

5.importBeanDefinitionResource() 解析import标签

6.processBeanDefinition() 解析bean标签

解析工作分为三步:

  1. 解析默认标签。
  2. 解析默认标签后下得自定义标签。
  3. 注册解析后的 BeanDefinition 。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {// <1> 如果解析成功,则返回 BeanDefinitionHolder 对象。而 BeanDefinitionHolder 为 name 和 alias 的 BeanDefinition 对象// 如果解析失败,则返回 null 。BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {// <2> 解析默认标签后下得自定义标签bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// <3> 进行 BeanDefinition 的注册// 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));}
}

-> 第1步:解析beanName

在这里插入图片描述

  1. 解析 idname 属性,确定 aliases 集合
  2. 检测 beanName 是否唯一
  3. 对属性进行解析并封装成 AbstractBeanDefinition 实例 beanDefinition
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {return parseBeanDefinitionElement(ele, null);
}@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {// 获取Id 和 nameString id = ele.getAttribute(ID_ATTRIBUTE);String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);// 解析nameList<String> aliases = new ArrayList<>();if (StringUtils.hasLength(nameAttr)) {// name属性可以存在多个,可以以不同的分隔符间隔,这里就是用于拆分的工具类String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}// bean名称优先使用idString beanName = id;if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {// 没有id,则默认使用别名的第1个beanName = aliases.remove(0);if (logger.isTraceEnabled()) {logger.trace("No XML 'id' specified - using '" + beanName +"' as bean name and " + aliases + " as aliases");}}// 检查bean是否存在重名现象if (containingBean == null) {checkNameUniqueness(beanName, aliases, ele);}// 解析属性,构造 AbstractBeanDefinition 对象AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);if (beanDefinition != null) {// beanName如果不存在,则需要系统生成if (!StringUtils.hasText(beanName)) {try {// containingBean传过来的就是Nullif (containingBean != null) {beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);}else {// 生成唯一的 beanNamebeanName = 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;
}

-> 第2步:解析内部标签

第2步的入口来自第1步
在这里插入图片描述

@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {this.parseState.push(new BeanEntry(beanName));// 解析 class 属性String className = null;if (ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim();}// 解析 parent 属性String parent = null;if (ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}try {// 创建用于承载属性的 AbstractBeanDefinition 实例// 创建的类型为: GenericBeanDefinitionAbstractBeanDefinition bd = createBeanDefinition(className, parent);// 解析默认 bean 的各种属性parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);// 提取 descriptionbd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));// tips:// 下面的一堆是解析 <bean>......</bean> 内部的子元素,// 解析出来以后的信息都放到 bd 的属性中// 解析元数据 <meta />parseMetaElements(ele, bd);// 解析 lookup-method 属性 <lookup-method />parseLookupOverrideSubElements(ele, bd.getMethodOverrides());// 解析 replaced-method 属性 <replaced-method />parseReplacedMethodSubElements(ele, bd.getMethodOverrides());// 解析构造函数参数 <constructor-arg />parseConstructorArgElements(ele, bd);// 解析 property 子元素 <property />parsePropertyElements(ele, bd);// 解析 qualifier 子元素 <qualifier />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;
}

-> 第3步:解析bean属性

第3步的入口来自第2步
在这里插入图片描述

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);}// 解析scope属性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());}// 解析abstract属性if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));}// 解析lazy-init属性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)));}// 解析init-method属性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);}// 解析destroy-method属性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;
}

7.registerBeanDefinition() 注册

从第6步开始,解析完bean标签的各个属性,然后就是要注册到容器中了

// BeanDefinitionReaderUtils#registerBeanDefinition()  该方式是1个工具类,一个中转而已public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// 获取beanName// 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);}}
}

-> registerBeanDefinition() 注册bean

处理过程如下:

  • <1> 对 BeanDefinition 进行校验,该校验也是注册过程中的最后一次校验了,主要是对 AbstractBeanDefinitionmethodOverrides 属性进行校验。
  • <2> 根据 beanName 从缓存中获取 BeanDefinition 对象。
  • <3> 如果缓存中存在,则根据 allowBeanDefinitionOverriding 标志来判断是否允许覆盖。如果允许则直接覆盖。否则,抛出 BeanDefinitionStoreException 异常。
  • <4>若缓存中没有指定 beanNameBeanDefinition,则判断当前阶段是否已经开始了 Bean 的创建阶段?如果是,则需要对 beanDefinitionMap 进行加锁控制并发问题,否则直接设置即可。
  • <5> 若缓存中存在该 beanName 或者单例 bean 集合中存在该 beanName ,则调用 #resetBeanDefinition(String beanName) 方法,重置 BeanDefinition 缓存。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {// <1> 校验 BeanDefinition 。// 校验 beanName 与 beanDefinition 非空Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");// 这是注册前的最后一次校验了,主要是对属性 methodOverrides 进行校验。if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}// <2> 从缓存中获取指定 beanName 的 BeanDefinitionBeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);// <3> 如果已经存在// 获取到则代表已经存在if (existingDefinition != null) {// 如果配置了不允许覆盖,则抛出异常if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}// 覆盖 beanDefinition 大于 被覆盖的 beanDefinition 的 ROLE ,打印 info 日志else if (existingDefinition.getRole() < beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (logger.isInfoEnabled()) {logger.info("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +existingDefinition + "] with [" + beanDefinition + "]");}}// 覆盖 beanDefinition 与 被覆盖的 beanDefinition 不相同,打印 debug 日志else if (!beanDefinition.equals(existingDefinition)) {if (logger.isDebugEnabled()) {logger.debug("Overriding bean definition for bean '" + beanName +"' with a different definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}// 其它,打印 debug 日志else {if (logger.isTraceEnabled()) {logger.trace("Overriding bean definition for bean '" + beanName +"' with an equivalent definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}// *************允许覆盖,直接覆盖原有的 BeanDefinition 到 beanDefinitionMap 中*************this.beanDefinitionMap.put(beanName, beanDefinition);}else {// <4> 如果未存在// 检测创建 Bean 阶段是否已经开启,如果开启了则需要对 beanDefinitionMap 进行并发控制if (hasBeanCreationStarted()) {// 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;removeManualSingletonName(beanName);}}else {// 注册到容器中this.beanDefinitionMap.put(beanName, beanDefinition);// 注册beanNamethis.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}if (existingDefinition != null || containsSingleton(beanName)) {// <5> 重新设置 beanName 对应的缓存resetBeanDefinition(beanName);}else if (isConfigurationFrozen()) {clearByTypeCache();}
}

-> registerAlias() 注册别名

把别名和beanName的映射关系添加到1个Map里面,key=别名,value=beanName

// SimpleAliasRegistry.java@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) {// name == alias 则去掉aliasif (alias.equals(name)) {this.aliasMap.remove(alias);if (logger.isDebugEnabled()) {logger.debug("Alias definition '" + alias + "' ignored since it points to same name");}}else {// 获取 alias 已注册的 beanNameString registeredName = this.aliasMap.get(alias);// 当注册的beanName存在if (registeredName != null) {// 相同,则 return ,无需重复注册if (registeredName.equals(name)) {// An existing alias - no need to re-registerreturn;}// 不允许覆盖,则抛出 IllegalStateException 异常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);// 注册 aliasthis.aliasMap.put(alias, name);if (logger.isTraceEnabled()) {logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");}}}
}

允许覆盖,则抛出 IllegalStateException 异常
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);
// 注册 alias
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace(“Alias definition '” + alias + “’ registered for name '” + name + “'”);
}
}
}
}

相关文章:

《Spring系列》第2章 解析XML获取Bean

一、基础代码 Spring加载bean实例的代码 public static void main(String[] args) throws IOException {// 1.获取资源Resource resource new ClassPathResource("bean.xml");// 2.获取BeanFactoryDefaultListableBeanFactory factory new DefaultListableBeanFa…...

小红书20230326暑假实习笔试

第一题&#xff1a;加密 小明学会了一种加密方式。他定义suc(x)为x在字母表中的后继&#xff0c;例如a的后继为b&#xff0c;b的后继为c… &#xff08;即按字母表的顺序后一个&#xff09;。特别的&#xff0c;z的后继为a。对于一个原字符串S&#xff0c;将其中每个字母x都替…...

【java】不要二、把字符串转成整数

目录 &#x1f525;一、编程题 1.不要二 2.把字符串转换成整数 &#x1f525;一、编程题 1.不要二 链接&#xff1a;不要二_牛客题霸_牛客网 (nowcoder.com) 描述&#xff1a;二货小易有一个W*H的网格盒子&#xff0c;网格的行编号为0~H-1&#xff0c;网格的列编号为0~W-1…...

数据的质量管控工作

数据的质量管控工作&#xff0c;整个工作应该围绕启动阶段制定的目标进行。适当引入一些质量管控工具可帮助我们更高效的完成工作。 第一步、数据剖析 首先应该进行已知数据问题的评估&#xff0c;这里评估的范围也应控制本轮管控的目标范围内。其次&#xff0c;通过对数据进行…...

【SpringBoot笔记29】SpringBoot集成RabbitMQ消息队列

这篇文章,主要介绍SpringBoot如何集成RabbitMQ消息队列。 目录 一、集成RabbitMQ 1.1、引入amqp依赖 1.2、添加连接信息 1.3、添加RabbitMQ配置类...

前端架构师-week2-脚手架架构设计和框架搭建

将收获什么 脚手架的实现原理 Lerna的常见用法 架构设计技巧和架构图绘制方法 主要内容 学习如何以架构师的角度思考基础架构问题 多 Package 项目管理痛点和解决方案&#xff0c;基于 Lerna 脚手架框架搭建 imooc-cli 脚手架需求分析和架构设计&#xff0c;架构设计图 附赠内…...

CMake项目实战指令详细分析

CMake是一个跨平台的自动化构建系统&#xff0c;可以用简单的语句来描述所有平台的编译过程。CMake可以输出各种各样的编译文件&#xff0c;如Makefile、VisualStudio等。 CMake主要是编写CMakeLists.txt文件&#xff0c;然后用cmake命令将CMakeLists.txt文件转化为make所需要的…...

【深度学习】——LSTM参数设置

批大小设置 LSTM的批大小可以根据训练数据集的大小和计算资源的限制来确定。一般而言&#xff0c;批大小越大&#xff0c;训练速度越快&#xff0c;但可能会导致过拟合和内存限制。批大小越小&#xff0c;训练速度越慢&#xff0c;但对于较大的数据集和内存限制较严格的情况下…...

计算机网络高频60问 背完差不多了!!

计算机网络高频60问 网络分层结构 计算机网络体系大致分为三种&#xff0c;OSI七层模型、TCP/IP四层模型和五层模型。一般面试的时候考察比较多的是五层模型。 五层模型&#xff1a;应用层、传输层、网络层、数据链路层、物理层。 应用层&#xff1a;为应用程序提供交互服务…...

路由策略小实验

实验要求&#xff1a; 1、R1环回使用重发布&#xff0c;R2和R3使用双向重发布 2、使用路由策略解决&#xff0c;选路不佳 第一步&#xff0c;基础配置 [R1]int l0 [R1-LoopBack0]ip add 1.1.1.1 24 [R1-LoopBack0]int g0/0/0 [R1-GigabitEthernet0/0/0]ip add 192.168.12.1 …...

C语言realloc背后的内存管理

malloc申请内存&#xff0c;但不初始化。 calloc申请内存&#xff0c;且初始化为0。 free释放内存。 realloc重新分配已经分配的内存空间&#xff0c;可以变小&#xff0c;也可以变大。 以前一直有一个疑问&#xff0c;realloc是不是经常失败&#xff1f; 其实&#xff0c;rea…...

GPT可以被放任的在问答区应用吗?

GPT可以被放任的在问答区应用吗&#xff1f;1、CSDN问答乱象2、GPT-4&#xff0c;大增长时代的序幕数字生命离我们到底还有多远&#xff1f;AI 家教/老师/教育 距离独立又有哪些需要完成的过程&#xff1f;3、老顾对CSDN问答的一些看法老顾对GPT使用者的一些建议1、CSDN问答乱象…...

限制网络接口的一些简介(一)

大家在上网的时候&#xff0c;我们设置了www&#xff0c;当有来自internet的www要求时&#xff0c;我们的主机就会予以响应。这是因为你的主机已经开启了www的监听端口。所以&#xff0c;当我们启用一个daemon时&#xff0c;就可能触发主机的端口进行监听的动作&#xff0c;此时…...

ChatGPT如何批量撰写最新的热点自媒体文章

如何用ChatGPT创作高质量的自媒体文章 自媒体已成为互联网上的一个重要组成部分&#xff0c;无论您是想在社交媒体、博客中发布内容&#xff0c;高质量的文章都是自媒体成功的重要组成部分。ChatGPT是一个智能文章生成器&#xff0c;能够帮助创作者快速、高效地生成高质量的自…...

GPT4和ChatGPT的区别,太让人震撼

文 | Serendipity知乎 前言 GPT4上午朋友圈已经刷屏啦&#xff0c;不过我还在忙&#xff0c;刚刚才登上 GPT-4 &#xff0c;现在来体验一下~ 附 GPT-4 能力测试站&#xff08;无需魔法&#xff0c;仅供国内研究测试&#xff09;&#xff1a; https://gpt4test.com 附 Cha…...

redis实战---分布式锁--单机篇

redis分布式锁故事背景什么是Redis分布式锁业务场景未加任何锁的代码单机情况下JVM级别加锁多服务部署总结提升故事背景 本篇文章是redis实战系列的第二篇文章。本章的主要内容是Redis分布式锁的相关知识。本篇文章将告诉你什么是分布式锁&#xff0c;结合一个业务场景&#x…...

Java正则表达式

Java 正则表达式 文章目录Java 正则表达式捕获组正则表达式语法Matcher 类的方法索引方法查找方法替换方法start 和 end 方法matches 和 lookingAt 方法replaceFirst 和 replaceAll 方法appendReplacement 和 appendTail 方法PatternSyntaxException 类的方法正则表达式是对字符…...

MySQL数据库之——高级[进阶]SQL语句(二)正则表达式和存储过程

文章目录一、正则表达式&#xff08;REGEXP&#xff09;1、正则表达式匹配符2、语法二、存储过程1、概述2、优点3、 创建、调用、查看和删除存储过程4、存储过程的控制语句一、正则表达式&#xff08;REGEXP&#xff09; 1、正则表达式匹配符 2、语法 SELECT 选项 FROM 表名 …...

Python基于周立功盒子的二次开发的准备工作

Python基于周立功盒子的二次开发的准备工作 一、基本介绍     基于周立功的二次开发是python通过调用zlgcan.dll,来实现CAN卡的通讯收发报文的,在python中通过ctypes模块调用c++动态库的接口函数(zlgcan.dll),我们需要根据我的电脑选择相对应版本的dll,比如64位的操…...

2023年PMP考生|考前必练全真模拟题分享,附答案解析

“日日行&#xff0c;不怕千万里&#xff1b;常常做&#xff0c;不怕千万事。”每日五题&#xff0c;备考无压力&#xff01; 1、敏捷项目以价值为驱动交付&#xff0c;确定好所有待办事项的价值进而去制造可交付成果。那么在整个敏捷项目周期中&#xff0c;衡量团队交付的可交…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...