《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
继承 AttributeAccessor
和 BeanMetadataElement
接口。两个接口定义如下:
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
ChildBeanDefinition
、RootBeanDefinition
、GenericBeanDefinition
三者都继承AbstractBeanDefinition
抽象类,即AbstractBeanDefinition
对三个子类的共同的类信息进行抽象。- 如果配置文件中定义了父 和 子 ,则父 用
RootBeanDefinition
表示,子 用ChildBeanDefinition
表示,而没有父 的就使用RootBeanDefinition
表示。 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类,
- 默认命名空间,例如:
<bean id="book" class="com.h3c.ss.Book"></bean>
- 自定义注解方式,例如:
<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标签
解析工作分为三步:
- 解析默认标签。
- 解析默认标签后下得自定义标签。
- 注册解析后的 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
- 解析
id
、name
属性,确定aliases
集合 - 检测
beanName
是否唯一 - 对属性进行解析并封装成
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
进行校验,该校验也是注册过程中的最后一次校验了,主要是对AbstractBeanDefinition
的methodOverrides
属性进行校验。 - <2> 根据
beanName
从缓存中获取BeanDefinition
对象。 - <3> 如果缓存中存在,则根据
allowBeanDefinitionOverriding
标志来判断是否允许覆盖。如果允许则直接覆盖。否则,抛出BeanDefinitionStoreException
异常。 - <4>若缓存中没有指定
beanName
的BeanDefinition
,则判断当前阶段是否已经开始了 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暑假实习笔试
第一题:加密 小明学会了一种加密方式。他定义suc(x)为x在字母表中的后继,例如a的后继为b,b的后继为c… (即按字母表的顺序后一个)。特别的,z的后继为a。对于一个原字符串S,将其中每个字母x都替…...

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

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

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

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

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

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

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

路由策略小实验
实验要求: 1、R1环回使用重发布,R2和R3使用双向重发布 2、使用路由策略解决,选路不佳 第一步,基础配置 [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申请内存,但不初始化。 calloc申请内存,且初始化为0。 free释放内存。 realloc重新分配已经分配的内存空间,可以变小,也可以变大。 以前一直有一个疑问,realloc是不是经常失败? 其实,rea…...

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

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

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

GPT4和ChatGPT的区别,太让人震撼
文 | Serendipity知乎 前言 GPT4上午朋友圈已经刷屏啦,不过我还在忙,刚刚才登上 GPT-4 ,现在来体验一下~ 附 GPT-4 能力测试站(无需魔法,仅供国内研究测试): https://gpt4test.com 附 Cha…...

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

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

MySQL数据库之——高级[进阶]SQL语句(二)正则表达式和存储过程
文章目录一、正则表达式(REGEXP)1、正则表达式匹配符2、语法二、存储过程1、概述2、优点3、 创建、调用、查看和删除存储过程4、存储过程的控制语句一、正则表达式(REGEXP) 1、正则表达式匹配符 2、语法 SELECT 选项 FROM 表名 …...

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

2023年PMP考生|考前必练全真模拟题分享,附答案解析
“日日行,不怕千万里;常常做,不怕千万事。”每日五题,备考无压力! 1、敏捷项目以价值为驱动交付,确定好所有待办事项的价值进而去制造可交付成果。那么在整个敏捷项目周期中,衡量团队交付的可交…...

Python入门教程+项目实战-7.1节: 条件控制结构
目录 7.1.1 理解条件控制 7.1.2 if,elif,else 7.1.3 条件表达式 7.1.4 条件控制可以嵌套 7.1.5 if语句的三元运算 7.1.6 系统学习python 7.1.1 理解条件控制 在日常生活中,我们常喜欢说如果, "如果怎么样,那么就会怎么样"。"如果&…...

【机器学习】P4 特征缩放与学习率
这里写自定义目录标题特征缩放标准化归一化平均值归一化收敛学习率特征缩放 特征缩放(Feature scaling)是一种数据预处理技术,它用于将不同尺度的特征值缩放到相同的范围内,以便更好地应用于机器学习算法中。在不进行特征缩放的情…...

《Python编程:从入门到实战》(第2版)学习笔记 第11章 测试代码
【写在前面】为进一步提高自己的python代码能力,打算把几本经典书籍重新过一遍,形成系统的知识体系,同时适当记录一些学习笔记,我尽量及时更新!先从经典的《Python编程:从入门到实战》书籍开始吧。有问题欢…...

SpringBoot(1)基础入门
SpringBoot基础入门SpringBoot项目创建方式Idea创建SpringBoot官网创建基于阿里云创建项目手工搭建SpringBoot启动parentstarter引导类内嵌tomcat基础配置属性配置配置文件分类yaml文件yaml数据读取整合第三方技术整合JUnit整合MyBatis整合Mybatis-Plus整合DruidSpringBoot是由…...

利用Flow Simulation快速经济高效地解决传热难题
几乎一切事物都会经历某种程度的发热或冷却,而且对于许多产品来说,热管理已成为避免过度发热和实现功能正常运行的一个关键要求。能够有效解决传热问题的制造商将会在竞争中占有明显的优势。利用一个简单易用的流体分析应用程序 SOLIDWORKS Flow Simulat…...

揭开二维码背后的神秘面纱用二维码识别 API 就够了
写在前面 二维码(QR code)已经成为现代生活中不可或缺的一部分。二维码具有可靠性、快速识别、易于存储等优点,因此在广泛应用于支付、门票、社交网络、广告等方面。但是,对于大多数人来说,二维码背后的编码方式是完全…...

系统分析——系统构建最重要的一环
🌟所属专栏:信息系统分析与设计 🐔作者简介:rchjr——五带信管菜只因一枚 😮前言:该系列将持续更新信息系统分析与设计课程的相关学习笔记,欢迎和我一样的小白订阅,一起学习共同进步…...

第1-第20个高级shell程序
高级shell脚本 1.使用Shell脚本批量修改文件名 #!/bin/bash for fi lein$(ls*.txt) do mv $file${file%%.*}.md done2.统计一个文本文件中某个单词出现的次数 #!/bin/bashword"example" count0 whilereadline do forwin$line do if["$w""$word&qu…...

【致敬嵌入式攻城狮第2期活动预热征文】学习安排
文章目录「 致敬未来的攻城狮计划 」——学习计划前言学习计划🚗单片机理论实践🚗学业阅读计划「 致敬未来的攻城狮计划 」——学习计划 🚀🚀开启攻城狮的成长之旅!这是我参与的由 CSDN博客专家 架构师李肯和 瑞萨MCU …...

035:cesium加载KML文件,显示图形
第035个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载KML文件, 显示图形。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共83行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:https:/…...