• java基础巩固-宇宙第一AiYWM:为了维持生计,Spring全家桶_Part1-3(学学Spring源码呗:默认的标签和自定义标签是咋解析的)~整起


    Part3:上一次说到了Spring的DefaultBeanDefinitionDocumentReader类中的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法,这个方法中引入了对两种不同标签的处理【当完成从配置文件到Document的转换并提取对应的root后,就开始了所有元素的解析,而在这一过程中便 开始了默认标签与自定义标签两种格式的区分】:其实这个过程也就是Spring将bean从配置文件到加载到内存中的全过程

    public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    ...
    	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    			//对beans的处理,可以看出就是当Spring拿到一个元素时首先要做的是根据命名空间进行解析
    			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 ele) {
    						//对bean的处理。对于根节点或者子节点如果是默认命名空间的话则采用parseDefaultElement方法进行解析,否则使用delegate.parseCustomElement方法对自定义命名空间进行解析
    						if (delegate.isDefaultNamespace(ele)) {
    							parseDefaultElement(ele, delegate);
    						}
    						//如果不是默认的命名空间而是自定义命名空间,则使用parseCustomElement方法进行元素解析
    						else {
    							//对bean的处理
    							delegate.parseCustomElement(ele);
    						}
    					}
    				}
    			}
    			else {
    				delegate.parseCustomElement(root);
    			}
    		}
    	}
    ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 像上面程序中那样,默认标签的解析是在parseDefaultElement函数中进行的,分别对4种不同标签(import、alias、bean和beans)做了不同的处理
      ...
      if (delegate.isDefaultNamespace(ele)) {
      	parseDefaultElement(ele, delegate);
      }
      ...
      private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
      	//对import标签的处理
      	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      		importBeanDefinitionResource(ele);
      	}
      	//对alias标签的处理
      	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      		processAliasRegistration(ele);
      	}
      	//对bean标签的处理
      	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      		processBeanDefinition(ele, delegate);
      	}
      	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      		// recurse,对beans标签的处理
      		doRegisterBeanDefinitions(ele);
      	}
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 对bean标签的处理:DefaultBeanDefinitionDocumentReader类中的processBeanDefinition(ele, delegate);
        在这里插入图片描述
        protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        		//进入BeanDefinition Delegate类的parseBeanDefinitionElement方法中可以看到。在这个方法中其实就是解析bean标签中的id属性、name属性、
        		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        		if (bdHolder != null) {
        			//如果需要的话就对beanDefinition进行装饰,当Spring中的bean使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时这句代码便会起作用了。首先获取属性或者元素的命名空间,以此来判断该元素或者属性是否适用于自定义标签的解析条件,找出自定义类型所对应的NamespaceHandler并进行进一步解析
        			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        			try {
        				// Register the final decorated instance.
        				//执行到这一步,配置文件中的东西该解析的也解析完了,该装饰的也装饰完了,此时得到的beanDefinition已经可以满足后续的使用要求了,唯一还剩下的工作就是注册bean,所以这一句代码或者说这一步就是来进行注册。在这个registerBeanDefinition方法中,使用beanName做唯一标识注册,解析的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中
        				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        			}
        			catch (BeanDefinitionStoreException ex) {
        				getReaderContext().error("Failed to register bean definition with name '" +
        						bdHolder.getBeanName() + "'", ele, ex);
        			}
        			// Send registration event.
        			//当程序开发人员需要对注册BeanDefinition事件进行监听时可以通过注册监听器的方式并将处理逻辑写入监听器中,目前在Spring中并没有对此事件做任何逻辑处理。
        			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        		}
        	}
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 除了解析提取元素中的id以及name等属性【当然也不止这俩属性,你想想呀,咱们经常见得还有啥:class的、property的、】,如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应【beanName = …generateBeanName(beanDefinition,…)】,然后最终将获取到的信息封装到BeanDefinitionHolder的实例中
          在这里插入图片描述
          在这里插入图片描述
          • 下面一大串都是对配置文件中的东东和西西进行解析和装饰的,那总会解析装饰完的那一天吧。咱们先看看解析装饰完了后会干什么。等执行到processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry())这一句代码时,配置文件中的东西该解析的也解析完了,该装饰的也装饰完了,此时得到的beanDefinition已经可以满足后续的使用要求了,唯一还剩下的工作就是注册bean,所以这一句代码或者说这一步就是来进行注册。在这个registerBeanDefinition方法中,使用beanName做唯一标识注册,解析的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中。看这个方法的源码可以知道,beanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册
            • 通过beanName注册BeanDefinition:Spring中对于beanDefinition的注册基本上就是将beanDefinition直接放入map中就好了,使用beanName作为key。只不过就算是个小学生干啥前也得做点预备东西干啥后也得检查检查,更何况Spring这一大家子呢,人家不得做做预备做做检查嘛。人家在代码中会对AbstractBeanDefinition属性中的methodOverrides校验,看methodOverrides是否与工厂方法并存或者methodOverrides对应方法根本不存在、有没有出现并发访问的情况【因为beanDefinitionMap是全局共享变量】、如果对应的BeanName已经注册且在配置中配置了bean不允许被覆盖,则抛出异常否则直接覆盖、加入map缓存以及清楚之前留下的对应的beanName的缓存
            • 通过别名的注册BeanDefinition:别名嘛,主要验证如果beanName与alias相同的话不记录alias,并删除对应的alias,不然你这个alias就成了脏数据了呗【alias与beanName相同情况处理。若alias与beanName并名称相同则不需要处理并删除掉原有alias。】、alias覆盖处理。若aliasName已经使用并已经指向了另一beanName则需要用户的设置进行处理、alias循环检查。当A->B存在时,若再次出现A->C->B时候则会抛出异常。
        • BeanDefinition:其中涉及到的这个 BeanDefinition接口【BeanDefinition是配置文件元素标签在容器中的内部表示形式,Spring通过BeanDefinition将配置文件中的配置信息转换为容器的内部表示,并将这些BeanDefiniton注册到BeanDefinitonRegistry中,元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和中的属性是一一对应的。】 很重要,BeanDefinition接口在Spring中存在三种实现:RootBeanDefinition、ChildBean Definition以及GenericBeanDefinition【要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例,然后解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中。】,三种实现均继承了AbstractBeanDefiniton,大部分的通用属性都保存在了AbstractBeanDefinition中
          • 【在配置文件中可以定义父和子,父用RootBeanDefinition表示,而子用ChildBeanDefiniton表示,而没有父的就使用RootBeanDefinition表示。AbstractBeanDefinition对两者共同的类信息进行抽象,大部分的通用属性都保存在了AbstractBeanDefinition中。】
          • Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息
          • 要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例,然后解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中。创建了bean信息的承载实例GenericBeanDefinition类型的实例后,便可以进行bean信息的各种属性解析了,而parseBeanDefinitionAttributes方法是对element所有元素属性进行解析,咱们可以看看咱们经常接触的属性的解析源码,如下:
            public class BeanDefinitionParserDelegate {
            	public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
            		@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
            	//解析singleton属性。scope与singleton两个属性只能指定其中之一,不可以同时出现,否则Spring将会报出异常
            	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));
            	}
            	//在嵌入beanDifinition情况下且没有单独指定scope属性则使用父类默认的属性
            	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();
            	}//若没有设置或设置成其他字符都会被设置为false
            	bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
            	//解析autowired属性
            	String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
            	bd.setAutowireMode(getAutowireMode(autowire));
            	//解析depends-on属性
            	if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
            		String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
            		bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
            	}
            	//解析autowired-candidate属性
            	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));
            	}
            	//解析primary属性
            	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);
            	}
            	//解析factory-method属性
            	if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
            		bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
            	}
            	//解析factory-bean属性
            	if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
            		bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
            	}
            
            	return bd;
            }
            			}
            
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13
            • 14
            • 15
            • 16
            • 17
            • 18
            • 19
            • 20
            • 21
            • 22
            • 23
            • 24
            • 25
            • 26
            • 27
            • 28
            • 29
            • 30
            • 31
            • 32
            • 33
            • 34
            • 35
            • 36
            • 37
            • 38
            • 39
            • 40
            • 41
            • 42
            • 43
            • 44
            • 45
            • 46
            • 47
            • 48
            • 49
            • 50
            • 51
            • 52
            • 53
            • 54
            • 55
            • 56
            • 57
            • 58
            • 59
            • 60
            • 61
            • 62
            • 63
            • 64
            • 65
            • 66
            • 67
            • 68
            • 69
            • 70
            • 71
            • 72
            • 73
            • 74
            • 75
            • 76
            • 77
            • 78
            • 79
            • 80
          • 除上面说的一些标签之外,还有比如对子元素meta属性的解析:
            在这里插入图片描述
          • 还有比如对constructor-arg,咱们注入方式有set方法注入、构造函数注入等,具体的大家可以看看spring左膀右臂之左膀IOC概念篇。那咱们趁此也看看这个构造函数注入的底层实现是啥呀【Spring是通过parseConstructorArgElements函数来实现对于constructor-arg子元素的解析的,这个函数中的逻辑其实就是遍历所有子元素并提取所有的constructor-arg,然后在parseConstructorArgElement方法中进行解析,解析不就是把这个constructor-arg中的index属性、name属性、type属性等提取出来,说是这样说,但是其实具体的解析代码并不是简简单单的for循环就完了,而是在parseConstructorArgElement中,parseConstructorArgElement中主要的逻辑也就是下面的看有没有在配置中指定了index属性两方面】:【先举个小例子,实现的功能就是对HelloBean自动寻找对应的构造函数,并在初始化的时候将设置的参数传入进去】
            在这里插入图片描述
            ...
            <beans>
            	
            	<bean id="helloBean" class='com. HelloBean">
            		'0'>
            			<value>YWMvalue>
            		constructor-arg>
            		<constructor-arg index='1'>
            			<value>HHBvalue>
            		constructor-arg>
            	bean>
            	...
            beans>
            ...
            
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13
            • 14
            • 以index属性为例子,如果配置中指定了index属性,你得先解析constructor-arg的子元素,然后就得使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素,然后将type、name和index属性一并封装在ConstructorArgumentValues.ValueHolder类型中并添加至当前BeanDefinition的constructorArgumentValues的indexedArgumentValues属性中。
            • 如果没有指定index属性,还是得先解析constructor-arg的子元素,然后再使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素,将type、name和index属性一并封装在ConstructorArgumentValues.ValueHolder类型中并添加至当前BeanDefinition的constructorArgumentValues的genericArgumentValues属性中
              在这里插入图片描述
            • 不管是指定了index属性还是没有在配置中指定了index属性,主要解析属性元素的语句就是Object value = parsePropertyValue(ele, ...);,这个parsePropertyValue方法中主要的如下面伪代码:
              @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.意思就是一个属性只能对应一种类型:ref、value、list等
              		NodeList nl = ele.getChildNodes();
              		Element subElement = null;
              		for (int i = 0; i < nl.getLength(); i++) {
              			Node node = nl.item(i);
              			//对应description或者meta不处理,略过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;
              				}
              			}
              		}
              		
              		//解析constructor-arg上的ref属性
              		boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
              		//解析constructor-arg上的value属性
              		boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
              		if ((hasRefAttribute && hasValueAttribute) ||
              				((hasRefAttribute || hasValueAttribute) && subElement != null)) {
              			error(elementName +
              					" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
              		}
              
              		if (hasRefAttribute) {
              			//red属性的处理,使用RuntimeBeanReference封装对应的ref名称
              			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;
              		}
              		else if (hasValueAttribute) {
              			//value属性的处理,使用TypedStringValue封装
              			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;
              		}
              	}
              
              • 1
              • 2
              • 3
              • 4
              • 5
              • 6
              • 7
              • 8
              • 9
              • 10
              • 11
              • 12
              • 13
              • 14
              • 15
              • 16
              • 17
              • 18
              • 19
              • 20
              • 21
              • 22
              • 23
              • 24
              • 25
              • 26
              • 27
              • 28
              • 29
              • 30
              • 31
              • 32
              • 33
              • 34
              • 35
              • 36
              • 37
              • 38
              • 39
              • 40
              • 41
              • 42
              • 43
              • 44
              • 45
              • 46
              • 47
              • 48
              • 49
              • 50
              • 51
              • 52
              • 53
              • 54
              • 55
              • 56
              • 57
              • 58
              • 59
            • 上面其实有咱们经常见的比如对中的ref属性的处理,本质上是使用RuntimeBeanReference封装对应的ref名称;再比如中value属性的处理,本质上使用的是TypedStringValue封装。
            • 而对下面这种带有集合的标签元素的处理如下:
              在这里插入图片描述
          • 而对property标签元素的处理中,是由parsePropertyElement函数完成了对property属性的提取,其实也就是一个for循环,遍历提取出所有property的子元素,然后在for循环中调用parsePropertyElement处理,将返回值使用PropertyValue进行封装,并记录在了BeanDefinition中的propertyValues属性中。
            在这里插入图片描述
        • AbstractBeanDefinition:上面巴拉巴拉由BeanDefinition引出来了一大串,也该说说这个AbstractBeanDefinition了。大部分的通用属性都保存在了AbstractBeanDefinition中,那都有哪些属性呢?看看源码呗。挑点常见的:
          public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
          		implements BeanDefinition, Cloneable {
          		...
          		//bean的作用范围,对应bean属性的scope
          		public static final String SCOPE_DEFAULT = "";
          		@Nullable
          		private String scope = SCOPE_DEFAULT;
          		
          		//是否是抽象,对应bean属性的abstract
          		private boolean abstractFlag = false;
          		
          		//是否延迟加载,对应bean属性的lazy-init
          		private Boolean lazyInit;
          		
          		//自动注入模式,对应bean属性的autowire
          		public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
          		private int autowireMode = AUTOWIRE_NO;
          		
          		//用来表示一个bean的实例化依靠另一个bean先实例化,对应bean的属性depend-on
          		private String[] dependsOn;
          		
          		//autowire-candidate属性设置为false,这样容器在查找自动装配对象时,将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者,但是该bean本身还是可以使用自动装配来注入其他bean的。
          		
          		private boolean autowireCandidate = true;
          		
          		//自动装配时当出现多个bean候选者时,将作为首选者,对应bean属性primary
          		private boolean primary = false;
          		
          		//记录构造函数注入属性,对应bean属性constructor -arg
          		private ConstructorArgumentValues constructorArgumentValues;
          		
          		//普通属性集合
          		private MutablePropertyValues propertyValues;
          		
          		//初始化方法,对应bean属性init- method
          		private String[] initMethodNames;
          		
          		//销毁方法,对应bean属性destory-method
          		private String[] destroyMethodNames;
          		
          		//是否执行init- method以及destory-method,程序设置
          		private boolean enforceInitMethod = true;
          		private boolean enforceDestroyMethod = true;
          		
          		//bean的描述信息
          		private String description;
          		...
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43
          • 44
          • 45
          • 46
          • 47
          • 48
      • 对alias标签的处理:
        • 在对bean进行定义时,除了使用id属性来指定名称之外,为了提供多个名称,可以使用alias标签来指定而所有的这些名称都指向同一个bean,在某些情况下提供别名非常有用,,比如为了让应用的每一个组件能更容易地对公共组件进行引用。我感觉就是给一帮不认识的人起个代号好记好认人而已。而 咱们又不可能在一开始就给所有的bean起好所有的别名,咱们肯定是想在这个地方或者这个时间段为别人或者别处的bean起个别名。所以应运而生,在XML配置文件中可以用单独的元素来完成bean别名的定义
          在这里插入图片描述
        • 这个标签的实现原理就是:将别名与beanName组成一对注册至registry中
          protected void processAliasRegistration(Element ele) {
          		//获取beanName
          		String name = ele.getAttribute(NAME_ATTRIBUTE);
          		//获取alias
          		String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
          		...
          		boolean valid = true;
          		if (valid) {
          			try {
          				//注册alias,将别名与beanName组成一对注册至registry中
          				getReaderContext().getRegistry().registerAlias(name, alias);
          			}
          		}
          	...
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
      • 对import标签的处理:
        • 一般import用来对大型项目分模块处理,使用import的方式导入有模块配置文件,以后若有新模块的加入,那就可以简单修改这个文件了。这样大大简化了配置后期维护的复杂度,并使配置模块化,易于管理
          在这里插入图片描述
        • 这个标签的实现原理就是:或者说解析
        • 获取resource属性所表示的路径—>解析路径中的系统属性,格式如“${user.dir}”—>判定location是绝对路径还是相对路径—>如果是绝对路径则递归调用bean的解析过程,进行另一次的解析—>如果是相对路径则计算出绝对路径并进行解析—>通知监听器,解析完成
    • 对beans标签的处理:
      • 嵌入式beans标签与单独的配置文件并没有太大的差别,就是递归调用beans的解析过程,一个套一个呗。
        在这里插入图片描述
  • 自定义标签的处理:delegate.parseCustomElement(ele);
    • 一般咱们都是先写好XML文件,然后再去解析定义好的XML配置文件。但是你想呀,人家Spring支持对自身进行修改和集成,也就是咱们前面说的倚天剑和屠龙刀,那不能只是通过继承或者实现接口对Spring进行扩展呀,如果我想扩展Spring自定义标签怎么办。有人说我不想扩展,就不想你能拿我怎么办,那不准你用注解情况下你咋使用事务,你不得找这个哥们:tx:annotation-driven:一般扩展步骤如下:
      • 创建一个需要扩展的组件,其实就是 **创建一个普通的用来接收配置文件的POJO**一堆属性、一堆get/set方法
      • 定义一个XSD【其实就是一个XML配置文件,有namsspace、属性】文件描述组件内容
      • 创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义,其实就是先从element中解析并提取对应的元素然后将提取的数据放入到BeanDefinitionBuilder中,待到完成所有的bean解析后将所有bean统一注册到beanFactory中。【XSD文件是XML DTD的替代者,使用XML Schema语言进行编写】
      • 创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注册到Spring容器。编写Spring.handlers和Spring.schemas文件,默认位置是在工程的/META-INF/文件夹下,也可以通过Spring的扩展或者修改源码的方式改变路径。
    • 由上面步骤也可以看出来 Spring加载自定义标签的大致流程是遇到自定义标签然后就去Spring.handlers和Spring.schemas中去到默认位置是/META-INF/下找对应的handler和XSD,进而又找到对应的handler以及解析元素的Parser,从而完成了整个自定义元素的解析,也就是说自定义与Spring中默认的标准配置不同在于Spring将自定义标签解析的工作委托给了用户去实现
    • 不管怎样,默认标签解析完后就到了自定义标签的解析,这篇blog最开始那里也有分流,对于自定义标签的解析的话会分流到parseCustomElement方法这里,咱们一步步继续往下看喽:
      @Nullable
      	public BeanDefinition parseCustomElement(Element ele) {
      		return parseCustomElement(ele, null);
      	}
      	/**
      	 * Parse a custom element (outside the default namespace).
      	 * @param ele the element to parse
      	 * @param containingBd the containing bean definition (if any),containingBd为父类bean,对顶层元素的解析应该设置为null
      	 * @return the resulting bean definition
      	 */
      	@Nullable
      	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
      		//根据对应的bean获取对应的命名空间。标签的解析是从命名空间的提起开始的,无论是区分Spring中默认标签和自定义标签还是区分自定义标签中不同标签的处理器都是以标签所提供的命名空间为基础的。如何提取对应元素的命名空间其实并不需要我们亲自去实现,在org.w3c.dom.Node中已经提供了方法供我们直接调用
      		String namespaceUri = getNamespaceURI(ele);
      		if (namespaceUri == null) {
      			return null;
      		}
      		//根据命名空间找到对应的NamespaceHandler,相当于根据命名空间解析对应的处理器。在readerContext初始化的时候其属性namespaceHandlerResolver已经被初始化为了DefaultNamespaceHandlerResolver的实例,这句话是不是很熟悉。所以,这里调用的resolve方法其实调用的是DefaultNamespaceHandlerResolver类中的方法。在这个resolve方法中的主要逻辑也就是获取所有已经配置的handler映射,然后根据命名空间找到对应的信息,Spring才能根据映射关系找到匹配的处理器。当获取到自定义的NamespaceHandler之后就可以进行处理器初始化【当得到自定义命名空间处理后会马上执行namespaceHandler.init()来进行自定义Bean DefinitionParser的注册。注册后,命名空间处理器就可以根据标签的不同来调用不同的解析器进行解析】并解析了
      		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
      		if (handler == null) {
      			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
      			return null;
      		}
      		//调用自定义的NamespaceHandler处理器进行解析。得到了解析器以及要分析的元素后,Spring就可以将解析工作委托给自定义解析器去解析了。此时的handler已经被实例化成为了我们自定义的MyXxxHandler了,而MyXxxHandler也已经完成了初始化的工作。解析工作也就是首先是寻找元素对应的解析器,进而调用解析器中的parse方法进行进一步解析。
      		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
      	}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 总结一下自定义标签处理过程就是 在这个处理过程中同样也是按照Spring中默认标签的处理方式进行,包括创建BeanDefinition以及进行相应默认属性的设置,对于这些工作Spring都默默地帮我们实现了,只是暴露出一些接口来供用户实现个性化的业务

默认标签和自定义标签的解析就是Spring将bean从配置文件到加载到内存中的全过程,那咱们把bean从配置文件中按照人家配置文件编写者的要求或者说设计想法抠出来并加载到内存中之后,该干吗了,不就是加载bean,然后把bean扔到Spring容器中,按需分配呗,下篇Part1-4见

巨人的肩膀:
(很多很多好的文章,特此感谢google以及B站各位前辈)
Spring源码深度解析

  • 相关阅读:
    【java期末复习题】第10章 Java输入与输出
    Java-根据模板生成PDF
    WebDAV之葫芦儿·派盘+墨阅
    folium 增加搜索、经纬度弹出,字段弹出的方法
    【HMS core】【ML kit】机器学习服务常见问题FAQ
    数据结构(二):括号匹配(C++,栈)
    Golang context包的源码分析
    小程序当前页面栈以及跳转
    postgis ST_ClipByBox2D用法
    【吃瓜之旅】第一二章吃瓜学习
  • 原文地址:https://blog.csdn.net/m0_52436398/article/details/126055604