Spring源码(六)loadBeanDefinitions方法

前言

在这里插入图片描述
上文介绍了除了loadBeanDefinitions方法的其他方法,再来复习一下。
首先会判断是否创建了一个工厂,如果没有,那么默认创建一个DefaultListableBeanFactory工厂,里面主要设置了允许循环依赖、可以覆盖以及忽视一些aware接口。然后给创建好的beanFactory设置专属的ID。然后就是可以定制化自己的BeanFactory了,可以改变两个参数,一个循环依赖,一个beanDefinitions的覆盖,这里可以作为一个扩展点。最后是将xml文件解析成document,解析document的每个元素并设置到beanDefinition,并加入到BeanFactory中。本文主要记录下loadBeanDefinitions的具体实现过程。

loadBeanDefinitions方法前准备操作

首先看下里面有什么
在这里插入图片描述
在这里插入图片描述
可以看到一共六个方法,第一个new XmlBeanDefinitionReader(beanFactory);这个方法就是我们常常说的BeanDefinitionReader方法,将xml文件读到beanDefinitionReader里,方便后续的处理。
第二个方法是beanDefinitionReader.setEnvironment(this.getEnvironment());,这就是简单的设置下beanDefinitionReader的环境,方便对某些东西进行替换工作。
第三个方法beanDefinitionReader.setResourceLoader(this);设置下资源加载器,这里把自己设置进去了,也就是xmlapplicationContext。
第四个方法beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));这个方法也就是我们访问xml文件时,都有它的格式规定,一般为xsd或者dtd,我们都会在xml文件上面定义一些网址,意思是xsd文件从这个网址上找,比如下面:
在这里插入图片描述
但是一般情况下,网址是不可访问的,这个时候,需要读取本地的dtd、xsd了,这个方法就是这个用途。
进入下(new ResourceEntityResolver(this))方法里面,发现
在这里插入图片描述
这里默认创建了dtd的处理器与xsd处理器,现在我们一般都是xsd,进入到第二个方法里看下,

在这里插入图片描述
这里就知道了,默认实在META-INF/spring.schemas里面找xsd对应的格式了。
我们找一下这个文件:
在这里插入图片描述
这里会发现有一堆的网络地址到本地的映射。
所以这个方法主要也是做了一些准备工作,将xsd或者dtd的本地文件准备一下。大致画了一下流程图形式,这里主要是xsd格式的流程图,如下图所示。
在这里插入图片描述

第5个方法initBeanDefinitionReader(beanDefinitionReader); 这个方法比较简单了,点进去看下
在这里插入图片描述
这里的validating就是我们在super时,默认设置了这个值为true,也就是这个方法呢设置了xml文件需要进行验证操作。
第6个方法loadBeanDefinitions方法,这个方法才是整个方法的核心,下面单独开一个小节记录下过程。

loadBeanDefinitions方法

这个方法重载特别多,我们先一步步看,首先将beanDefinitionReader当作参数传进去,看到如下代码:
在这里插入图片描述
这里我们会获取getConfigLoations,这个东西实际上是在refresh方法前一个方法,setConfigLocations设置进去了,当时我们设置的其实是String[]数组形式,所以这里会走第二个判断条件。此时会将String[]数组传入到loadBeanDefinitions里,看下这个代码:
在这里插入图片描述
这里较为简单了,单纯的将String数组里面的元素取出来,再利用loadBeandefinitions重载方法,将String类型放进去。进入到此方法中:

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				int count = loadBeanDefinitions(resources);
				if (actualResources != null) {
					Collections.addAll(actualResources, resources);
				}
				if (logger.isTraceEnabled()) {
					logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
				}
				return count;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			Resource resource = resourceLoader.getResource(location);
			int count = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
			}
			return count;
		}
	}

这个方法我们看下,因为classPathxmlapplicationContext是属于ResourceLoader的,所以会进入if()判断里面来,所以Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);这个方法比较有意思,看下执行流程:
在这里插入图片描述
这里的resourcePatternResolver我们在super时,已经设置过了,是ant风格表达式,所以跳过,后面的getResources方法呢,在里面对locations匹配,比如是否以classpath:开头,最终返回一个Resource[]数组。
继续往下看,又会进入到loadBeanDefinitions重载里面,将Resource数组传进去,下面我们不用看就知道还是循环,取出resource对象。进入到重载方法里面。
在这里插入图片描述
继续进入到重载方法,参数为resource里面:

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();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		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());
			}
			return 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();
			}
		}
	}

这里进入到了一个loadBeanDefinitions,参数为encode的重载方法中。
首先是this.resourcesCurrentlyBeingLoaded.get();方法,这个是threadLocal类型的,获取已经加载的资源,这里没有,则为空,当前线程没有做过任何处理工作。

然后是inputStream,从encodedResource中获取已经封装的Resource对象并再次从Resource中获取其中的inputStream。读取了配置文件。
然后到达了核心步骤,doLoadBeanDefinitions。
下面看下这个核心方法doLoadBeanDefinitions。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
			Document doc = doLoadDocument(inputSource, resource);
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
	}

doLoadDocument(inputSource, resource);这个方法是做什么的?因为上一步inputStream读进来的都是字符串,不能进行识别,所以我们需要对这些字符串转成可以识别的对象,所以这里读成了document,看下这个方法。

在这里插入图片描述
前面3个参数都是前面设置好了,这里不多说了,而getValidationModeForResource(resource)则是否需要对xml文件进行验证,并决定哪种格式,如果xsd,那么返回3,如果dtd,返回2等等。
继续进入到loadDocument方法中去:
在这里插入图片描述
这里创建了一个工厂,根据这个工厂创建了builder,然后就是解析的过程,也就是parse方法。后面整体的解析工作建议不用看了,意义不大。

这里返回到doloadBeanDefinitions方法中,可以看下是否包含document信息。
在这里插入图片描述
我们解析好了document后,应该预测下后面会做什么事,应该要解析document的父元素是什么,子元素是什么,还有标签里面的属性又是什么。

总结下上面到底做了什么:,从String[] -string-Resource[]- resource,最终开始将resource读取成一个document文档,根据文档的节点信息封装成一个个的BeanDefinition对象。

然后完成了document方法的执行,下面是registerBeanDefinitions方法。
在这里插入图片描述
这里需要对document进行解析,所以创建了documentR解析的对象。
下面,getRegistry,这个就是我们的defaultListableBeanFactory,这里会获取到beanDefinitionMap的size,这里为0.
继续下面,这里registerBeanDefinitions的重载方法,点进入会发现这里有doRegisterBeanDefinitions。

在这里插入图片描述
进入到doRegisterBeanDefinitions方法中去:

	protected void doRegisterBeanDefinitions(Element root) {
		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)) {
					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;
	}

创建了一个委托,去解析,处理工作。
这里会判断是否是默认的命名空间,一般后面的流程spring没有的,也就是是否是profile,所以跳过。
下面的preProcessxml于postProcessxml在springMVC里面扩展。
而中间这个方法就比较重要了,parseBeanDefinitions。

loadBeanDefinitions——》dololadBeanDefinitions——》document——》registerBeanDefinitions——》doregisterBeanDefinitions——》parseBeanDefinitions

	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);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

这里主要是if里面判断的逻辑,这里会判断是否是默认空间,这里的默认空间里面有哪些标签呢?我们往下debug可以看到进入了parseDefaultElement方法。
在这里插入图片描述
这里就是默认的命名空间,除了Import、bean、alias、nested_beans_element之外,都不是默认的命名空间。
如果不是
在这里插入图片描述
如果不是默认的命名空间,那么需要获取对应的命名空间,根据命名空间找到处理器,再解析。
我们这里用了bean标签,所以进默认的命名空间,然后进行判断,会进入到processBeanDefinition(ele, delegate)方法。
在这里插入图片描述 beanDefinitionHolder是beanDefinition对象的封装类,封装了BeanDefinition,bean的名字和别名,用它来完成向IOC容器的注册得到这个beanDefinitionHolder就意味着beandefinition是通过BeanDefinitionParserDelegate对xml元素的信息按照spring的bean规则进行解析得到的。
这里就是解析元素的方法。
在这里插入图片描述
继续进来

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
		List<String> aliases = new ArrayList<>();
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			beanName = aliases.remove(0);
			if (logger.isTraceEnabled()) {
				logger.trace("No XML 'id' specified - using '" + beanName +
						"' as bean name and " + aliases + " as aliases");
			}
		}

		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}

		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
						beanName = this.readerContext.generateBeanName(beanDefinition);

						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;
	}

首先获取bean的id,name以及别名,检查名字是否唯一。
下面会看到一个parseBeanDefinitionElement方法的重载,因为现在只是解析了3个,其他元素并没有解析,所以这里需要对bean详细的解析。进入到此重载方法可以看到:

	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 {
			// 创建装在bean信息的AbstractBeanDefinition对象,实际的实现是GenericBeanDefinition
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

			// 解析bean标签的各种其他属性
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			// 设置description信息
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

			// 解析元数据
			parseMetaElements(ele, bd);
			// 解析lookup-method属性
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			// 解析replaced-method属性
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

			// 解析构造函数参数
			parseConstructorArgElements(ele, bd);
			// 解析property子元素
			parsePropertyElements(ele, bd);
			// 解析qualifier子元素
			parseQualifierElements(ele, bd);

			bd.setResource(this.readerContext.getResource());
			bd.setSource(extractSource(ele));

			return bd;
		}

在这个方法中,解析好了class与parent属性后,会创建一个beanDefinition,这里默认创建的是GenericBeanDefinition(),并在里面设置了beanClass以及parent。

在这里插入图片描述
但是可以看到bd的其他属性并没有设置,所以下面就是设置bd的其他属性。这里略过,返回至parseBeanDefinitionElement方法中,beanDefinition已经解析完成了,这个时候需要放到beanFactory里面了。返回至processBeanDefinition,在BeanDefinitionHolder生成后面继续看下面代码。
decorateBeanDefinitionIfRequired这个是装饰的方法,一般不需要装饰的。
继续向下,registerBeanDefinition,往beanfactory里面注册了。

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

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {

				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		// 处理注册已经注册的beanName情况
		if (existingDefinition != null) {

			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {

				if (logger.isInfoEnabled()) {
					logger.info("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(existingDefinition)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			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 {
				// Still in startup registration phase

				this.beanDefinitionMap.put(beanName, beanDefinition);
				// 记录beanName
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (existingDefinition != null || containsSingleton(beanName)) {

			resetBeanDefinition(beanName);
		}
		else if (isConfigurationFrozen()) {
			clearByTypeCache();
		}
	}

这里有获取beanDefinition时,会判断是否允许覆盖,这个参数实际上我们已经在obtainFreshBeanFactory()设置为true了。
然后放进beanDefinitionMap与beanDefinionNames里面去了,这里完成了意味着已经将、放进beanfactory里面了。

这里就是所有的loadBeanDefinions所有内容了,下一个需要记录下怎么自定义xml标签。

总结

  1. loadBeanDefinions重载方法的流程:

在这里插入图片描述
上面是loadBeanDefinions重载方法的流程,这里记录下,其实记这个流程就行,内部的基本都是千篇一律。

  1. 下面是loadbeanDefinions解析xml文件整体流程:
    在这里插入图片描述
    上面是loadbeanDefinions解析xml文件整体流程,如果缩略的来说,流程是loadBeanDefinitions ——> dololadBeanDefinitions——> document ——> registerBeanDefinitions ——> doregisterBeanDefinitions ——> parseBeanDefinitions ——> processBeanDefinion ——> BeanDefinionHolder ——> registerBeanDefinition.
    仔细的来说,在loadBeanDefinions(EncodeResurce)里面,会将xml用inputStream读进来,并包装成inputSource类,然后调用此方法的doloadBeanDefinions,这里主要做了两个事,一个是将xml文件解析成document结构,并返回一些节点信息;其次注册BeanDefinions,包含了对document文件的解析、设置到beandefinion、设置到beanFactory。在registerBeanDefinions里,创建了document的解析器,然后调用registerBeanDefinitions重载方法完成解析过程,这里会再调用doRegisterBeanDefinitions,这个方法做的事情主要是解析beanDefinions,调用了parseBeanDefinitions方法,这个方法会根据是否是默认的命名空间,决定走哪个方法,如果是,那么走parseDefaultElement,然后如果是bean标签,那么走processBeanDefinition方法,这个方法很重要,包含了document解析,并设置到beanDefinion中,这个方法是返回了beanDinfinionHolder;还有一个作用,将解析完毕的beanDefinion,放到两个集合中去,一个为BeandefinionMap与BeanDefinionNames中,相当于设置到了BeanFactory里了。
    在这里插入图片描述
    至此,loadBeanFactory结束。
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
代码解析2,部分摘抄 简单的说,在web容器中,通过ServletContext为Spring的IOC容器提供宿主环境,对应的建立起一个IOC容器的体系。其中,首先需要建立的是根上下文,这个上下文持有的对象可以有业务对象,数据存取对象,资源,事物管理器等各种中间层对象。在这个上下文的基础上,和web MVC相关还会有一个上下文来保存控制器之类的MVC对象,这样就构成了一个层次化的上下文结构。在web容器中启动Spring应用程序就是一个建立这个上下文体系的过程。Spring为web应用提供了上下文的扩展接口 WebApplicationContext: 如转载请注明,转载自:关注Java[http://www.gbsou.com] 本文链接: http://www.gbsou.com/2009/08/11/214.html - - Java代码 public interface WebApplicationContext extends ApplicationContext { //这里定义的常量用于在ServletContext中存取根上下文 String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; …… //对WebApplicationContext来说,需要得到Web容器的ServletContext ServletContext getServletContext(); } public interface WebApplicationContext extends ApplicationContext { //这里定义的常量用于在ServletContext中存取根上下文 String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; ...... //对WebApplicationContext来说,需要得到Web容器的ServletContext ServletContext getServletContext(); } 而一般的启动过程,Spring会使用一个默认的实现,XmlWebApplicationContext – 这个上下文实现作为在web容器中的根上下文容器被建立起来,具体的建立过程在下面我们会详细分析。 Java代码 public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { /** 这是和web部署相关的位置信息,用来作为默认的根上下文bean定义信息的存放位置*/ public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"; public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml"; //我们又看到了熟悉的loadBeanDefinition,就像我们前面对IOC容器的分析中一样,这个加载工程在容器的refresh()的时候启动。 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { //对于XmlWebApplicationContext,当然使用的是XmlBeanDefinitionReader来对bean定义信息来进行解析 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); } protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { } //使用XmlBeanDefinitionReader来读入bean定义信息 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { for (int i = 0; i < configLocations.length; i++) { reader.loadBeanDefinitions(configLocations[i]); } } } //这里取得bean定义信息位置,默认的地方是/WEB-INF/applicationContext.xml protected String[] getDefaultConfigLocations() { if (getNamespace() != null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; } else { return new String[] {DEFAULT_CONFIG_LOCATION}; } } } public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { /** 这是和web部署相关的位置信息,用来作为默认的根上下文bean定义信息的存放位置*/ public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"; public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml"; //我们又看到了熟悉的loadBeanDefinition,就像我们前面对IOC容器的分析中一样,这个加载工程在容器的refresh()的时候启动。 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { //对于XmlWebApplicationContext,当然使用的是XmlBeanDefinitionReader来对bean定义信息来进行解析 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); } protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { } //使用XmlBeanDefinitionReader来读入bean定义信息 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { for (int i = 0; i < configLocations.length; i++) { reader.loadBeanDefinitions(configLocations[i]); } } } //这里取得bean定义信息位置,默认的地方是/WEB-INF/applicationContext.xml protected String[] getDefaultConfigLocations() { if (getNamespace() != null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; } else { return new String[] {DEFAULT_CONFIG_LOCATION}; } } } 对于一个Spring激活的web应用程序,可以通过使用Spring代码声明式的指定在web应用程序启动时载入应用程序上下文(WebApplicationContext),Spring的ContextLoader是提供这样性能的类,我们可以使用 ContextLoaderServlet或者ContextLoaderListener的启动时载入的Servlet来实例化Spring IOC容器 – 为什么会有两个不同的类来装载它呢,这是因为它们的使用需要区别不同的Servlet容器支持的Serlvet版本。但不管是 ContextLoaderSevlet还是 ContextLoaderListener都使用ContextLoader来完成实际的WebApplicationContext的初始化工作。这个ContextLoder就像是Spring Web应用程序在Web容器中的加载器booter。当然这些Servlet的具体使用我们都要借助web容器中的部署描述符来进行相关的定义。 下面我们使用ContextLoaderListener作为载入器作一个详细的分析,这个Servlet的监听器是根上下文被载入的地方,也是整个 Spring web应用加载上下文的第一个地方;从加载过程我们可以看到,首先从Servlet事件中得到ServletContext,然后可以读到配置好的在web.xml的中的各个属性值,然后ContextLoder实例化WebApplicationContext并完成其载入和初始化作为根上下文。当这个根上下文被载入后,它被绑定到web应用程序的ServletContext上。任何需要访问该ApplicationContext的应用程序代码都可以从WebApplicationContextUtils类的静态方法来得到:
课程资源[[03 08:58:22,348 INFO ] com.kedou.mq.listener.SimpleMessageListener$1.run(SimpleMessageListener.java:125) - Listener on test.tcp.user_sync is started. [[03 08:58:22,349 INFO ] com.kedou.mq.listener.SimpleMessageListener$1.run(SimpleMessageListener.java:130) - Listener on test.tcp.user_sync is started. 03-Jul-2018 08:58:22.411 INFO [localhost-startStop-1] org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [333] milliseconds. [[03 08:58:22,462 INFO ] org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:485) - FrameworkServlet 'hessian': initialization started [[03 08:58:22,466 INFO ] org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:511) - Refreshing WebApplicationContext for namespace 'hessian-servlet': startup date [Tue Jul 03 08:58:22 CST 2018]; parent: Root WebApplicationContext [[03 08:58:22,467 INFO ] org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from file [/home/tomcats/tomcat-9109-tcp/webapps/tcp-innerapi/WEB-INF/classes/servlet/hessian-servlet.xml] [[03 08:58:22,539 INFO ] org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.<init>(AutowiredAnnotationBeanPostProcessor.java:153) - JSR-330 'javax.inject.Inject' annotation found and supported for autowiring [[03 08:58:23,230 INFO ] org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.registerHandler(AbstractUrlHandlerMapping.java:314) - Mapped URL path [/schoolApiService.hs] onto handler '/schoolApiService.hs' [[03 08:58:23,231 INFO ] org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.registerHandler(AbstractUrlHandlerMapping.java:314) - Mapped URL path [/cardUserApiService.hs] onto handler '/cardUserApiService.hs' [[03 08:58:23,231 INFO ] org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.registerHandler
rar包内含有spring2.5.6源码,解压即可使用 源代码分析,是一件既痛苦又快乐的事情,看别人写的代码是通过的,但当你能够看明白的时候,相信快乐也会随之而来,为了减少痛苦,更快的带来快乐,在这里希望通过这篇文章对觉得困难的朋友有一个帮助。 本文以spring框架的XmlBeanFactory为入手点进行分析,希望能够以尽量简洁明了的方式给予有需要的朋友一定的帮助。 首先来打开该类的代码,我们将看到如下代码: Java代码 public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } } public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } } 这个类的代码很简单,一个成员对象加两个构造函数,从这里我们可以看出,最重要的地方在于最后一个构造函数: Java代码 super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); 第一句就是将父亲工厂交给父类的构造函数,实际上最后也就是把父工厂保存到类的parentBeanFactory成员对象中,这个对象是在AbstractBeanFactory抽象类中定义的,而这个父工厂也会一直传递到该抽象类进行保存。第二句就是整个类中最重要的地方了,顾名思义,它的目的是通过XmlBeanDefinitionReader这个XML的Reader从资源resource中(也就是你的配置文件)读取bean的定义。接下来我们打开XmlBeanDefinitionReader的loadBeanDefinitions方法,我们可看到在这个方法里代码就一行,调用了一个同名不同参的方法,而参数是EncodedResource的一个实例,这个类实际上是Resource的一个包装类,用来保存资源的Encode的,那接下来我们再看被调用的loadBeanDefinitions方法,这个方法里最主要的部分就是: Java代码 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 这里的目的是将资源包装成一个InputSource,连同Resource作为参数传递到doLoadBeanDefinitions方法 Java代码 DocumentBuilderFactory factory = createDocumentBuilderFactory(); if (logger.isDebugEnabled()) { logger.debug("Using JAXP implementation [" + factory + "]"); } DocumentBuilder builder = createDocumentBuilder(factory); Document doc = builder.parse(inputSource); return registerBeanDefinitions(doc, resource); DocumentBuilderFactory factory = createDocumentBuilderFactory(); if (logger.isDebugEnabled()) { logger.debug("Using JAXP implementation [" + factory + "]"); } DocumentBuilder builder = createDocumentBuilder(factory); Document doc = builder.parse(inputSource); return registerBeanDefinitions(doc, resource); 这个方法的目的一目了然,就是为了将资源解释成为Document对象,然后调用registerBeanDefinitions方法,这里不做详细解释,不了解的话请去看看关于JAXP的介绍。接下来我们打开registerBeanDefinitions方法Java代码 public int registerBeanDefinitions(Document doc, Resource resource) throws BeansException { XmlBeanDefinitionParser parser = (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass); return parser.registerBeanDefinitions(this, doc, resource); } public int registerBeanDefinitions(Document doc, Resource resource) throws BeansException { XmlBeanDefinitionParser parser = (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass); return parser.registerBeanDefinitions(this, doc, resource); } 这里创建了一个XmlBeanDefinitionParser接口的实现,这个接口的具体类是DefaultXmlBeanDefinitionParser,这个接口很简单,只有registerBeanDefinitions一个方法,这个方法的作用也很明了,就是用来注册Bean的定义的,所以说类和方法的名字一定要起得有意义,这样可以让人一看就大概了解其作用,减少了很多阅读代码的痛苦。废话不多说,我们打开DefaultXmlBeanDefinitionParser的registerBeanDefinitions方法,这个类就是解释XML配置文件的核心类了,打开registerBeanDefinitions方法后我们看到如下代码: Java代码 public int registerBeanDefinitions(BeanDefinitionReader reader, Document doc, Resource resource) throws BeanDefinitionStoreException { this.beanDefinitionReader = reader; this.resource = resource; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); //初始化根元素 initDefaults(root); if (logger.isDebugEnabled()) { logger.debug("Default lazy init '" + getDefaultLazyInit() + "'"); logger.debug("Default autowire '" + getDefaultAutowire() + "'"); logger.debug("Default dependency check '" + getDefaultDependencyCheck() + "'"); } preProcessXml(root);//一个空方法用于扩展 int beanDefinitionCount = parseBeanDefinitions(root);//解释配置的主要方法 if (logger.isDebugEnabled()) { logger.debug("Found " + beanDefinitionCount + " elements in " + resource); } postProcessXml(root); //一个空方法用于扩展 return beanDefinitionCount; } public int registerBeanDefinitions(BeanDefinitionReader reader, Document doc, Resource resource) throws BeanDefinitionStoreException { this.beanDefinitionReader = reader; this.resource = resource; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); //初始化根元素 initDefaults(root); if (logger.isDebugEnabled()) { logger.debug("Default lazy init '" + getDefaultLazyInit() + "'"); logger.debug("Default autowire '" + getDefaultAutowire() + "'"); logger.debug("Default dependency check '" + getDefaultDependencyCheck() + "'"); } preProcessXml(root);//一个空方法用于扩展 int beanDefinitionCount = parseBeanDefinitions(root);//解释配置的主要方法 if (logger.isDebugEnabled()) { logger.debug("Found " + beanDefinitionCount + " elements in " + resource); } postProcessXml(root); //一个空方法用于扩展 return beanDefinitionCount; } 在这个方法当中,主要用于解释定义的有两个方法,一个是initDefaults,一个是parseBeanDefinitions,第一个方法是用来解释根元素的属性的,例如lazy-init, autowire等,而parseBeanDefinitions就是用来解释具体的bean定义了,方法代码如下: Java代码 protected int parseBeanDefinitions(Element root) throws BeanDefinitionStoreException { NodeList nl = root.getChildNodes(); int beanDefinitionCount = 0; for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (IMPORT_ELEMENT.equals(node.getNodeName())) { importBeanDefinitionResource(ele); } else if (ALIAS_ELEMENT.equals(node.getNodeName())) { String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); this.beanDefinitionReader.getBeanFactory().registerAlias(name, alias); } else if (BEAN_ELEMENT.equals(node.getNodeName())) { beanDefinitionCount++; BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false); BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory()); } } } return beanDefinitionCount; } protected int parseBeanDefinitions(Element root) throws BeanDefinitionStoreException { NodeList nl = root.getChildNodes(); int beanDefinitionCount = 0; for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (IMPORT_ELEMENT.equals(node.getNodeName())) { importBeanDefinitionResource(ele); } else if (ALIAS_ELEMENT.equals(node.getNodeName())) { String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); this.beanDefinitionReader.getBeanFactory().registerAlias(name, alias); } else if (BEAN_ELEMENT.equals(node.getNodeName())) { beanDefinitionCount++; BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false); BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory()); } } } return beanDefinitionCount; } 其他标签具体如何被解释这里就不多说,相信大家也能看得懂,这里主要讲一下解释bean的的处理,我们注意以下代码: Java代码 else if (BEAN_ELEMENT.equals(node.getNodeName())) { beanDefinitionCount++; BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false); BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory()); } else if (BEAN_ELEMENT.equals(node.getNodeName())) { beanDefinitionCount++; BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false); BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory()); } 这里是当碰到一个bean标签的时候所进行的处理,也既是对bean的定义进行解释,可以看到parseBeanDefinitionElement方法的第一个参数就是bean则个元素,第二个参数表示该bean是否为内置的bean,从这里进行解释的bean都不可能是内置的,所以这里直接以false为参数,打开parseBeanDefinitionElement方法,就可以看到这个方法里就是对bean的内部的解释,也很简单,也不多讲了,呵呵(下班时间已经到了,所以就写这么多了,基本的流程也就这样,没什么特别难的地方。),对了,最后还有一点就是解释完后,bean的定义将会被保存到beanFactory中,这个beanFactory的实现就是XmlBeanFactory了,该beanFactory是在new的时候被传递到reader中的,就是该类中以下这行代码: Java代码 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); 好了,就这么多了,本文只作为参考,只讲解了如何加载bean定义这块,只作为一个参考,希望对其他朋友能有所帮助吧,因为时间匆忙,有错漏的地方请指正。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值