跟着大佬阅读spring源码(二)

上一篇LK沿着各位大佬的足迹,探索了一下IOC容器在初始化的时候如何定位到配置文件的路径。
接着上片继续说,实际上IOC在定位容器时使用到了Delegate委派模式,这个模式是怎么回事呢?
委派模式:抽象类和子类都有相同的方法,调用抽象类的方法而在子类中具体去实现他们。
来看看类图:
在这里插入图片描述
代码实现:
抽象类Animal

public abstract class Animal {
	void eat() {
	};

	void sleep() {
	};
}

Dog:

public class Dog extends Animal{
     void eat() {
    	 System.out.println("狗吃骨头了");
     }
     
     void sleep() {
    	 System.out.println("狗要睡觉了");
     }
}

Cat:

public class Cat extends Animal{
	  void eat() {
	    	 System.out.println("猫吃骨头了");
	     }
	     
	     void sleep() {
	    	 System.out.println("猫要睡觉了");
	     }
}

调用实现

public class Test {
	
	public static void main(String[] args) {
		Animal animal = null;
		/**
		 * 使用时在抽象类中调用方法,而在其子类去实现。
		 */
		while(true) {
			if(new Random().nextBoolean()) {
				animal = new Dog();
				animal.eat();
				animal.sleep();
			}else {
				animal = new Cat();
				animal.eat();
				animal.sleep();
			}
		}
	
	}
	
}

结果:
在这里插入图片描述
可以看到委派的优势:对内隐藏实现, 易于扩展; 简化调用。定位资源使用的refreshBeanFactory()方法就是在其子类AbstractRefreshableApplicationContext中具体实现的。

相信看完这个对理解源码是不是又容易了一点呢?

  • BeanDefinition的载入和解析

接着上一篇IOC定位到资源,就开始解析配置文件内容,将XMl转换为Document对象。
跟着LK的脚步继续往下看
追溯上一篇在AbstractBeanDefinitionReader的loadBeanDefinitions方法中我们得到了定位resource的方法.getResources和getResource

	public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
		Resource resource = resourceLoader.getResource(location);
		return loadCount;
		}

那么解析方法在哪呢,在它的子类XmlBeanDefinitionReader的loadBeanDefinitions方法中实现

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//此处是始化资源集合,将资源添加到集合中
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
	
		try {
		//此处是解析的开始,将定位到的资源解析为IO流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				//在此方法中进行IO流和DoM的转换
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
	
	}

看到这就知道这个方法是处理定位到资源的入口,主要干了两件事1.将资源转换成IO流,2.将流解析成Dom。来看看具体解析流的过程

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
		//将IO流解析成DOm对象
			Document doc = doLoadDocument(inputSource, resource);
		//将Dom元素注册进bean元素中
			return registerBeanDefinitions(doc, resource);
		}
	
	}

来看看具体转换实现,此实现是在DefaultDocumentLoader的loadDocument方法中实现的。

	public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
		if (logger.isDebugEnabled()) {
			logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
		return builder.parse(inputSource);
	}

看到这有没有想起LK的简单版springIoc实现,此方法先创建DocumentBuilderFactory工厂,在工厂中产生DocumentBuilder对象,最后执行parse()方法解析IO流。具体解析过程不在这列出。到这已经得到了Dom对象。

扫描二维码关注公众号,回复: 8812859 查看本文章

得到DOm对象就开始解析Bean的各种配置,回到XmlBeanDefinitionReader的doLoadBeanDefinitions方法中的 registerBeanDefinitions注册bean定义的方法中。

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	     //从xml文档读取bean的定义
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //配置文档环境
		documentReader.setEnvironment(getEnvironment());
		//得到容器中定义的bean 数量
		int countBefore = getRegistry().getBeanDefinitionCount();
		//从给定的DOM文档中读取bean定义,将解析到的属性注册到bean中。
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//返回解析注册的bean数量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

这个方法包含dom解析的方法入口registerBeanDefinitions。进来看看它都有什么,在此我只想说兄弟挺住真的有点多。
在它的实现类DefaultBeanDefinitionDocumentReader中具体实现registerBeanDefinitions。

	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		//得到DOM根节点元素
		Element root = doc.getDocumentElement();
		//解析DOM元素
		doRegisterBeanDefinitions(root);
	}

具体解析类:

	protected void doRegisterBeanDefinitions(Element root) {
	 //递归解析<beans>
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		if (this.delegate.isDefaultNamespace(root)) {
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					return;
				}
			}
		}
//处理XML中之前自定义的元素
		preProcessXml(root);
		//从Document的根元素开始进行Bean定义的Document对象 解析
		parseBeanDefinitions(root, this.delegate);
	//处理XML中之后自定义的元素	
		postProcessXml(root);

		this.delegate = parent;
	}

//该方法--从Document的根元素开始进行Bean定义的Document对象 解析
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//如果是默认的命名空间
		if (delegate.isDefaultNamespace(root)) {
		//获得所有子节点
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
			//得到每一个子节点
				Node node = nl.item(i);
				//匹配是Element元素
				if (node instanceof Element) {
				//将其转换成dom元素
					Element ele = (Element) node;
					//如果是默认的命名空间
					if (delegate.isDefaultNamespace(ele)) {
					//解析默认元素
						parseDefaultElement(ele, delegate);
					}
					else {
					//Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的解析规则解析Document根节点 
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
		//Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的解析规则解析Document根节点 
			delegate.parseCustomElement(root);
		}
	}

上面方法开始DOM根元素开始解析bean,匹配到spring默认的命名规则。下面我们看一个Bean的匹配

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		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
			doRegisterBeanDefinitions(ele);
		}
	}
//处理给定的bean元素,分析bean定义,并在注册处注册。
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	//BeanDefinitionParserDelegate解析document中bean元素属性
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
		//处理自定义的属性和嵌套的元素
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// 向Spring IoC容器注册解析得到的Bean定义
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// 发送注册事件
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

//document文档中bean元素属性的解析
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//得到bean id 和  命名空间
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//创建别名集合
		List<String> aliases = new ArrayList<String>();
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		 //如果<Bean>元素中没有配置id属性时,将别名中的第一个值赋值给beanName
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			beanName = aliases.remove(0);
			if (logger.isDebugEnabled()) {
				logger.debug("No XML 'id' specified - using '" + beanName +
						"' as bean name and " + aliases + " as aliases");
			}
		}
 //检查<Bean>元素所配置的id或者name的唯一性,
		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}
  //详细对<Bean>元素中配置的Bean定义进行解析的地方【property,description,parent等】
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
					  //如果<Bean>元素中没有配置id、别名或者name,且没有包含子
                      //<Bean>元素,为解析的Bean生成一个唯一beanName并注册 
                      //可以看到它是先去寻找ParentName,如果有,命名为ParentName+$child"
                      //否则去找FactoryBeanName,命名为FactoryBeanName+$created
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
						beanName = this.readerContext.generateBeanName(beanDefinition);
					//如果<Bean>元素中没有配置id、别名或者name,但包含了子
                      //<Bean>元素,为解析的Bean使用别名向IoC容器注册 
						String beanClassName = beanDefinition.getBeanClassName();
						if (beanClassName != null &&
								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
						}
					}
					if (logger.isDebugEnabled()) {
						logger.debug("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;
	}

看到这大家终于知道我们在配置文件中定义的bean id 和 name 都是在什么时候解析的了吧,我只想说妈呀,终于看到一点希望。
但这还没完,找到bean了,那它的属性标签property等都是在哪解析的呢?
来看看上文提到的parseBeanDefinitionElement这个方法

//解析Docunment文档中bean元素的其他属性标签,不考虑名称或别名
	public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {
		this.parseState.push(new BeanEntry(beanName));
		try {
			String parent = null;
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT))
			parseMetaElements(ele, bd);
			//解析LookupOverrideSub
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			//解析ReplacedMethod
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            //解析ConstructorArg
			parseConstructorArgElements(ele, bd);
			//解析Property
			parsePropertyElements(ele, bd);
			//解析Qualifier
			parseQualifierElements(ele, bd);
			bd.setResource(this.readerContext.getResource());
			bd.setSource(extractSource(ele));
			return bd;
		}
		return null;
	}

//来看看它是如何解析Property的,其它都大同小异
//解析每一个propetry元素
	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);
			}
		}
	}
//解析具体的一个property属性
	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;
		}
		//创建PropertyEntry对象放入栈中
		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);
			//属性赋值给PropertyValue对象
			PropertyValue pv = new PropertyValue(propertyName, val);
			parseMetaElements(ele, pv);
			pv.setSource(extractSource(ele));
			bd.getPropertyValues().addPropertyValue(pv);
		}
		finally {
		//属性解析完之后出栈
			this.parseState.pop();
		}
	}

//获取具体的属性key,value属性值,这里是“meta”
	public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
		NodeList nl = ele.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
				Element metaElement = (Element) node;
				String key = metaElement.getAttribute(KEY_ATTRIBUTE);
				String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
				BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
				attribute.setSource(extractSource(metaElement));
				attributeAccessor.addMetadataAttribute(attribute);
			}
		}
	}

到此DOM解析告一段落,属性解析流程和bean解析相同,一层一层解析。画个图来梳理一下整个解析过程
在这里插入图片描述
还有最后一块IOC初始化就大功告成了,老铁们加油

  • BeanDefinition的注册
    需要注意的是这是将bean的定义注入IOC容器并不是依赖注入的实现。
    再来看看DefaultBeanDefinitionDocumentReader中的processBeanDefinition方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// 最终修饰的实例
				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));
		}
	}
//在bean工厂中注册bean
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// .beanName作为key来保存bean实例
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// 用别名注册
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

再来看看注册完如何使用,具体实现在其子类DefaultListableBeanFactory(有三个我们只看其中的一个)
在这里插入图片描述

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		BeanDefinition oldBeanDefinition;
//从Map中通过beanName获取bean实例
		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
	
		}
		else {
		//beanName不为空,加到beanDefinitionNames集合中
			this.beanDefinitionNames.add(beanName);
			//用完删除
			this.manualSingletonNames.remove(beanName);
			//在缓存中移除
			this.frozenBeanDefinitionNames = null;
		}
		this.beanDefinitionMap.put(beanName, beanDefinition);

		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}

至此整个注册过程完成。主要完成一下几点

  1. 在map集合中以beanName存放beanDifinition.
  2. 在子类中取用。
  3. 在缓存和set集合中移除使用过的beanDifinition

有没有一种释怀的感觉,哈哈那你就错了,LK还要带你梳理一下整个流程

  1. 首先定位Resource,具体看getResources和getResource方法实现。
  2. 解析Dom。
  3. 注册beanDifinition。
    我们见证了bean的变化

在这里插入图片描述

最后自此感谢大佬 田小波,I,Frankenstein等

发布了47 篇原创文章 · 获赞 18 · 访问量 5710

猜你喜欢

转载自blog.csdn.net/yuruizai110/article/details/93739820
今日推荐