SPRING原理解析-Ioc容器初始化

       IoC容器的初始化就是含有BeanDefinition信息的Resource的定位、载入、解析、注册四个过程,最终我们配置的bean,以beanDefinition的数据结构存在于IoC容器即内存中。这里并不涉及bean的依赖注入,只是bean定义的载入。但有例外,在使用Ioc容器时有一个预实例化的配置,即bean定义中的设置了lazyinit属性,那么这个bean在Ioc容器初始化时就预先加载,不需要等到Ioc整个初始化后,第一次getBean时才会触发。其中refresh()启动对Ioc容器的初始化。
      第一过程是Resource定位过程。这个Resource定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用提供了统一接口。对于这些BeanDefinition的存在形式,相信大家都不会感到陌生。比如说,在文件系统中的Bean定义信息可以使用FileSystemResource来进行抽象;在类路径中可以使用前面提到的ClassPathResource来使用,等等。这个过程类似于容器寻找数据的过程,就像用水桶装水先要把水找到一样。 
       第二个关键的部分是BeanDefinition的载入,该载入过程把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition,下面可以看到这个数据结构的详细定义。总地说来,这个BeanDefinition实际上就是POJO对象在IoC容器中的抽象,这个BeanDefinition定义了一系列的数据来使得IoC容器能够方便地对POJO对象也就是Spring的Bean进行管理。即BeanDefinition就是Spring的领域对象。下面我们会对这个载入的过程进行详细的分析,便于大家对整个过程有比较清楚的了解。 
      第三个过程是向IoC容器注册这些BeanDefinition的过程。这个过程是通过调用BeanDefinitionRegistry接口的实现来完成的,这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。可以看到,在IoC容器内部将BeanDefinition注入到一个HashMap中去,Ioc容器是通过这个HashMap来持有这些BeanDefinition数据的。
BeanDefinition的Resource的定位

在AbstractBeanDefinitionReader.loadBeanDefinitions()方法中,调用了defaultResourceLoader的getResource()方法,这里即为resouce的定位,如下
public Resource getResource(String location) {  
        Assert.notNull(location, "Location must not be null");  
        if (location.startsWith(CLASSPATH_URL_PREFIX)) {  
            return new  
ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());  
        }  
        else {  
            try {  
                // Try to parse the location as a URL...  
                URL url = new URL(location);  
                return new UrlResource(url);  
            }  
            catch (MalformedURLException ex) {  
                // No URL -> resolve as resource path.  
                return getResourceByPath(location);  
            }  
        }  
    }    

getResourceByPath的实现如下:
protected Resource getResourceByPath(String path) {  
        return new ClassPathContextResource(path, getClassLoader());  
    } 

beanDefinition的载入
beanDefinition的载入在XmlBeanDefinitionReader中的loadBeanDefinitions()方法实现:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
        Assert.notNull(encodedResource, "EncodedResource must not be null");  
        if (logger.isInfoEnabled()) {  
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
        }  
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
        if (currentResources == null) {  
            currentResources = new HashSet<EncodedResource>(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();  
            try {  
                InputSource inputSource = new InputSource(inputStream);  
                if (encodedResource.getEncoding() != null) {  
                    inputSource.setEncoding(encodedResource.getEncoding());  
                }  
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  
            }  
            finally {  
                inputStream.close();  
            }  
        }  
        catch (IOException ex) {  
            throw new BeanDefinitionStoreException(  
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);  
        }  
        finally {  
            currentResources.remove(encodedResource);  
            if (currentResources.isEmpty()) {  
                this.resourcesCurrentlyBeingLoaded.remove();  
            }  
        }  
    }  

注:上面的代码中提到,获得xml文件,得到IO的InputSOurce准备进行读取,具体的读取在doLoadBeanDefinitions()方法中找到,这是从特定的xml文件中实际载入BeanDefinition的地方;解析的一步在documentLoader中,也就是defaultDocumentLoader按照xml解析得到Document对象,而第二步,defaultBeanDefinitionDocumentreader按照Spring的bean规则把从Document对象解析为BeanDefinition,最后BeanDefinition对象集合到封装类BeanDefinitionHolder中。

其中,doLoadBeanDefinitions()方法在该类中实现如下

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  
            throws BeanDefinitionStoreException {  
        try {  
            int validationMode = getValidationModeForResource(resource);  
            Document doc = this.documentLoader.loadDocument(  
                    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());  
            return registerBeanDefinitions(doc, resource);  
        }  
        catch(){  
        //异常处理   
}  
} 


其中,registerBeanDefinitions()方法在该类中实现如下(解析第二步):

//按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//获得容器中注册的Bean数量
		int countBefore = getRegistry().getBeanDefinitionCount();
//解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,//具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//统计解析的Bean数量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
//创建BeanDefinitionDocumentReader对象,解析Document对象
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
		return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
	}


DefaultBeanDefinitionDocumentReader 对Bean定义的Document对象解析:

BeanDefinitionDocumentReader接口通过registerBeanDefinitions方法调用其实现类DefaultBeanDefinitionDocumentReader对Document对象进行解析,解析的代码如下:

//根据Spring DTD对Bean的定义规则解析Bean定义Document对象
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		//获得XML描述符
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		//获得Document的根元素
		Element root = doc.getDocumentElement();
		//具体的解析过程由BeanDefinitionParserDelegate实现,
//BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素
		BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
		//在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性
		preProcessXml(root);
		//从Document的根元素开始进行Bean定义的Document对象
		parseBeanDefinitions(root, delegate);
		//在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性
postProcessXml(root);
	}
	//创建BeanDefinitionParserDelegate,用于完成真正的解析过程
	protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) {
		BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
		//BeanDefinitionParserDelegate初始化Document根元素
		delegate.initDefaults(root);
		return delegate;
	}
	//使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//Bean定义的Document对象使用了Spring默认的XML命名空间
		if (delegate.isDefaultNamespace(root)) {
			//获取Bean定义的Document对象根元素的所有子节点
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				//获得Document节点是XML元素节点
				if (node instanceof Element) {
					Element ele = (Element) node;
				//Bean定义的Document的元素节点使用的是Spring默认的XML命名空间
					if (delegate.isDefaultNamespace(ele)) {
						//使用Spring的Bean规则解析元素节点
						parseDefaultElement(ele, delegate);
					}
					else {
//没有使用Spring默认的XML命名空间,则使用用户自定义的解//析规则解析元素节点
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			//Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的
			//解析规则解析Document根节点
			delegate.parseCustomElement(root);
		}
	}
	//使用Spring的Bean规则解析Document元素节点
	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>元素,
//按照Spring的Bean规则解析元素
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
	}
	//解析<Import>导入元素,从给定的导入路径加载Bean定义资源到Spring IoC容器中
	protected void importBeanDefinitionResource(Element ele) {
		//获取给定的导入元素的location属性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
		//如果导入元素的location属性值为空,则没有导入任何资源,直接返回
if (!StringUtils.hasText(location)) {
			getReaderContext().error("Resource location must not be empty", ele);
			return;
		}
		//使用系统变量值解析location属性值
		location = SystemPropertyUtils.resolvePlaceholders(location);
		Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
//标识给定的导入元素的location是否是绝对路径
		boolean absoluteLocation = false;
		try {
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		}
		catch (URISyntaxException ex) {
			//给定的导入元素的location不是绝对路径
		}
		//给定的导入元素的location是绝对路径
		if (absoluteLocation) {
			try {
				//使用资源读入器加载给定路径的Bean定义资源
				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
				}
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error(
						"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
			}
		}
		else {
			//给定的导入元素的location是相对路径
			try {
				int importCount;
				//将给定导入元素的location封装为相对路径资源
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				//封装的相对路径资源存在
				if (relativeResource.exists()) {
					//使用资源读入器加载Bean定义资源
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				//封装的相对路径资源不存在
				else {
					//获取Spring IoC容器资源读入器的基本路径
					String baseLocation = getReaderContext().getResource().getURL().toString();
					//根据Spring IoC容器资源读入器的基本路径加载给定导入
					//路径的资源
					importCount = getReaderContext().getReader().loadBeanDefinitions(
							StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
				}
			}
			catch (IOException ex) {
				getReaderContext().error("Failed to resolve current resource location", ele, ex);
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
						ele, ex);
			}
		}
		Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
		//在解析完<Import>元素之后,发送容器导入其他资源处理完成事件
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}
	//解析<Alias>别名元素,为Bean向Spring IoC容器注册别名
	protected void processAliasRegistration(Element ele) {
		//获取<Alias>别名元素中name的属性值
String name = ele.getAttribute(NAME_ATTRIBUTE);
//获取<Alias>别名元素中alias的属性值
		String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
		boolean valid = true;
		//<alias>别名元素的name属性值为空
if (!StringUtils.hasText(name)) {
			getReaderContext().error("Name must not be empty", ele);
			valid = false;
		}
		//<alias>别名元素的alias属性值为空
		if (!StringUtils.hasText(alias)) {
			getReaderContext().error("Alias must not be empty", ele);
			valid = false;
		}
		if (valid) {
			try {
				//向容器的资源读入器注册别名
				getReaderContext().getRegistry().registerAlias(name, alias);
			}
			catch (Exception ex) {
				getReaderContext().error("Failed to register alias '" + alias +
						"' for bean with name '" + name + "'", ele, ex);
			}
			//在解析完<Alias>元素之后,发送容器别名处理完成事件
			getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
		}
	}
	//解析Bean定义资源Document对象的普通元素
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		// BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
//对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
	//向Spring IoC容器注册解析得到的Bean定义,这是Bean定义向IoC容器注册的入口			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			//在完成向Spring IoC容器注册解析得到的Bean定义之后,发送注册事件
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

通过上述Spring IoC容器对载入的Bean定义Document解析可以看出,我们使用Spring时,在Spring配置文件中可以使用<Import>元素来导入IoC容器所需要的其他资源,Spring IoC容器在解析时会首先将指定导入的资源加载进容器中。使用<Ailas>别名时,Spring IoC容器首先将别名元素所定义的别名注册到容器中。

对于既不是<Import>元素,又不是<Alias>元素的元素,即Spring配置文件中普通的<Bean>元素的解析由BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法来实现,代码如下:

//解析<Bean>元素的入口
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
		return parseBeanDefinitionElement(ele, null);
	}
//解析Bean定义资源文件中的<Bean>元素,这个方法中主要处理<Bean>元素的id,name
//和别名属性
	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
		//获取<Bean>元素中的id属性值
		String id = ele.getAttribute(ID_ATTRIBUTE);
		//获取<Bean>元素中的name属性值
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
		获取<Bean>元素中的alias属性值
		List<String> aliases = new ArrayList<String>();
		//将<Bean>元素中的所有name属性值存放到别名中
if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_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的唯一性,containingBean标识<Bean>
		//元素中是否包含子<Bean>元素
		if (containingBean == null) {
			//检查<Bean>元素所配置的id、name或者别名是否重复
			checkNameUniqueness(beanName, aliases, ele);
		}
		//详细对<Bean>元素中配置的Bean定义进行解析的地方
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
//如果<Bean>元素中没有配置id、别名或者name,且没有包含子//<Bean>元素,为解析的Bean生成一个唯一beanName并注册
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
//如果<Bean>元素中没有配置id、别名或者name,且包含了子//<Bean>元素,为解析的Bean使用别名向IoC容器注册
						beanName = this.readerContext.generateBeanName(beanDefinition);
						//为解析的Bean使用别名注册时,为了向后兼容									//Spring1.2/2.0,给别名添加类名后缀
						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);
		}
		//当解析出错时,返回null
		return null;
	}
//详细对<Bean>元素中配置的Bean定义其他属性进行解析,由于上面的方法中已经对//Bean的id、name和别名等属性进行了处理,该方法中主要处理除这三个以外的其他属性数据
public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {
		//记录解析的<Bean>
		this.parseState.push(new BeanEntry(beanName));
//这里只读取<Bean>元素中配置的class名字,然后载入到BeanDefinition中去
		//只是记录配置的class名字,不做实例化,对象的实例化在依赖注入时完成
		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}
		try {
			String parent = null;
			//如果<Bean>元素中配置了parent属性,则获取parent属性的值
			if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
				parent = ele.getAttribute(PARENT_ATTRIBUTE);
			}
			//根据<Bean>元素配置的class名称和parent属性值创建BeanDefinition
			//为载入Bean定义信息做准备
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//对当前的<Bean>元素中配置的一些属性进行解析和设置,如配置的单态(singleton)属性等
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
	//为<Bean>元素解析的Bean设置description信息	bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
			//对<Bean>元素的meta(元信息)属性解析
			parseMetaElements(ele, bd);
			//对<Bean>元素的lookup-method属性解析
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			//对<Bean>元素的replaced-method属性解析
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
			//解析<Bean>元素的构造方法设置
			parseConstructorArgElements(ele, bd);
			//解析<Bean>元素的<property>设置
			parsePropertyElements(ele, bd);
			//解析<Bean>元素的qualifier属性
			parseQualifierElements(ele, bd);
			//为当前解析的Bean设置所需的资源和依赖对象
			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();
		}
		//解析<Bean>元素出错时,返回null
		return null;
	}

只要使用过Spring,对Spring配置文件比较熟悉的人,通过对上述源码的分析,就会明白我们在Spring配置文件中<Bean>元素的中配置的属性就是通过该方法解析和设置到Bean中去的。

注意:在解析<Bean>元素过程中没有创建和实例化Bean对象,只是创建了Bean对象的定义类BeanDefinition,将<Bean>元素中的配置信息设置到BeanDefinition中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象。

上面方法中一些对一些配置如元信息(meta)、qualifier等的解析,我们在Spring中配置时使用的也不多,我们在使用Spring的<Bean>元素时,配置最多的是<property>属性,因此我们下面继续分析源码,了解Bean的属性在解析时是如何设置的。

5.BeanDefinitionParserDelegate解析<property>元素:

BeanDefinitionParserDelegate在解析<Bean>调用parsePropertyElements方法解析<Bean>元素中的<property>属性子元素,通过源码分析,我们可以了解在Spring配置文件中,<Bean>元素中<property>元素的相关配置是如何处理的:

a. ref被封装为指向依赖对象一个引用。

b.value配置都会封装成一个字符串类型的对象。

c.ref和value都通过“解析的数据类型属性值.setSource(extractSource(ele));”方法将属性值/引用与所引用的属性关联起来。

在方法的最后对于<property>元素的子元素通过parsePropertySubElement方法解析,我们继续分析该方法的源码,了解其解析过程。

6.解析<property>元素的子元素:

在BeanDefinitionParserDelegate类中的parsePropertySubElement方法对<property>中的子元素解析,通过源码分析,我们明白了在Spring配置文件中,对<property>元素中配置的Array、List、Set、Map、Prop等各种集合子元素的都通过上述方法解析,生成对应的数据对象,比如ManagedList、ManagedArray、ManagedSet等,这些Managed类是Spring对象BeanDefiniton的数据封装,对集合数据类型的具体解析有各自的解析方法实现,解析方法的命名非常规范,一目了然,我们对<list>集合元素的解析方法进行源码分析,了解其实现过程。

7.解析<list>子元素:

在BeanDefinitionParserDelegate类中的parseListElement方法就是具体实现解析<property>元素中的<list>集合子元素。

经过对Spring Bean定义资源文件转换的Document对象中的元素层层解析,Spring IoC现在已经将XML形式定义的Bean定义资源文件转换为Spring IoC所识别的数据结构——BeanDefinition,它是Bean定义资源文件中配置的POJO对象在Spring IoC容器中的映射,我们可以通过AbstractBeanDefinition为入口,荣IoC容器进行索引、查询和操作。

通过Spring IoC容器对Bean定义资源的解析后,IoC容器大致完成了管理Bean对象的准备工作,即初始化过程,但是最为重要的依赖注入还没有发生,现在在IoC容器中BeanDefinition存储的只是一些静态信息,接下来需要向容器注册Bean定义信息才能全部完成IoC容器的初始化过程。


 beanDefinition的解析
BeanDefinitionParserDelegate是一个重要的辅助类,它实现了对具体Bean元素在Bean定义信息的解析。
InputSource(InputStream)获取resource对象,接着使用DocumentLoader来对XML形式的Bean定义信息进行读入,转换成DOM数据。
BeanDefinition的载入分两部分,首先通过调用xml的解析器得到document,对象,但这些document对象并没有按照Spring的Bean规则进行解析。在完成
通用的xml解析以后,才是按照Spring的Bean规则进行解析的地方,这个地方就是DomcumentReader中完成BeanDefinition的处理。处理的结果由BeanDefinitionHolder对象持有,这个BeanDefinitionHolder对象还持有BeanDefinition使用相关的信息,比如Bean名字,别名id,name等属性元素,这些属性的解析还是比较简单的。对于其他元素配置的解析,比如各种Bean的属性配置,通过一个较复杂的解析过程,这个过程是友parseBeanDefinitionElement来完成的。解析完成后,会放到BeanDefinition对象中并设置到BeanDefinitionHolder中去。
beanDefinition的解析是在BeanDefinitionParserDelegate类的parseBeanDefinitionElement(Elementele, BeanDefinition containingBean)方法中完成。该方法首先调用了
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);  

获取bean的id和bean的name。接着,调用
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean)  ;

对bean元素进行详细解析,调用parseBeanDefinitionAttributes()解析bean的属性;
parsePropertyElements(ele, bd)解析bean的property。
bean属性就是注入scope、isLazyInit、autowire、sigleton等属性,都是按照类似方法来做:
if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
   // Spring 2.x "scope" attribute
  beanDefinition.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}

调用beanDefinition的set方法来实现。
解析propterty方法具体在parsePropertyElements()方法中,主要:
1.  bean定义中如果有同名的propterty,那么只解析第一个property,对于后续的同名propterty不做任何处理。

2. 需要判断是ref还是value。


 BeanDefinition的注册

解析过后的BeanDefinition在IoC容器中的注册:

让我们继续跟踪程序的执行顺序,接下来会到我们第3步中分析DefaultBeanDefinitionDocumentReader对Bean定义转换的Document对象解析的流程中,在其parseDefaultElement方法中完成对Document对象的解析后得到封装BeanDefinition的BeanDefinitionHold对象,然后调用BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器注册解析的Bean,BeanDefinitionReaderUtils的注册的源码如下:

当调用BeanDefinitionReaderUtils向IoC容器注册解析的BeanDefinition时,真正完成注册功能的是DefaultListableBeanFactory。

9.DefaultListableBeanFactory向IoC容器注册解析后的BeanDefinition:

DefaultListableBeanFactory中使用一个HashMap的集合对象存放IoC容器中注册解析的BeanDefinition,向IoC容器注册的主要源码如下:

//存储注册的俄BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
//向IoC容器注册解析的BeanDefiniton
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");
		//校验解析的BeanDefiniton
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}
		//注册的过程中需要线程同步,以保证数据的一致性
		synchronized (this.beanDefinitionMap) {
			Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
			//检查是否有同名的BeanDefinition已经在IoC容器中注册,如果已经注册,
			//并且不允许覆盖已注册的Bean,则抛出注册失败异常
			if (oldBeanDefinition != null) {
				if (!this.allowBeanDefinitionOverriding) {
					throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
							"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
							"': There is already [" + oldBeanDefinition + "] bound.");
				}
				else {//如果允许覆盖,则同名的Bean,后注册的覆盖先注册的
					if (this.logger.isInfoEnabled()) {
						this.logger.info("Overriding bean definition for bean '" + beanName +
								"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
					}
				}
			}
			//IoC容器中没有已经注册同名的Bean,按正常注册流程注册
			else {
				this.beanDefinitionNames.add(beanName);
				this.frozenBeanDefinitionNames = null;
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
			//重置所有已经注册过的BeanDefinition的缓存
			resetBeanDefinition(beanName);
		}
	}
 最后就通过beanDefinitionMap来操作beanDefinition了。

Ioc容器的实例化与依赖注入

Spring用CGLIB对Bean进行实例化,CGLIB是一个常用的字节码生成器的类库,它提供了一系列的API来提供生产和转换Java的字节码的功能。在Spring Aop中也使用CGLIB对java的字节码进行增强。在IOc容器中,要了解怎么使用CGLIB来生成Bean,需要了解SimpleInstantiationStrategy类,该Strategy是Spring用来生成Bean对象的默认类,它提供了两种实例化java对象的方法,一个是通过beanUtils,它使用jvm的反射功能,一种是通过前面提到的CGLIB(比如Enhance类)来生成。

依赖注入的发生实在BeanWrapper的setPropertyValues中实现的,具体的完成却是在BeanWrapper的子类BeanWrapperImpl中实现的。
在Bean的创建和对象依赖注入的过程中,需要依据BeanDefinition中的信息来递归地完成依赖注入。


 IoC容器的启动
FileSystemXmlApplicationContext的构造函数如下,启动了IoC容器。
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)  
            throws BeansException {  
        super(parent);  
        setConfigLocations(configLocations);  
        if (refresh) {  
            refresh();  
        }  
    }  

refresh()方法在FileSystemXmlApplicationContext的父类AbstractApplicationContext中实现。在refresh()方法中,最终调用了refreshBeanFactory()方法。
完整IoC容器启动分析(以FileSystemXmlApplicationContext为例)
1.  FileSystemXmlApplicationContext构造方法调用refresh()方法,这里是载入beanDefinition的入口
2. refresh()方法在FileSystemXmlApplicationContext的父类
AbstractApplicationContext中的实现,调用refreshBeanFactory()方法;
3. refreshBeanFactory()方法在AbstractApplicationContext的子类AbstractRefreshableApplicationContext中实现,调用了loadBeanDefinition()方法,启动对beanDefinition的载入;
4. loadBeanDefinition()在AbstractXmlApplicationContext中实现,调用了XmlBeanDefinitionReader的loadBeanDefinitions()方法。
5. XmlBeanDefinitionReader的loadBeanDefinitions()实现了对于承载beanDefnition定义的xml文件的读入,以I/O的方式。
6. 读入后,对beanDefinition进行解析。Bean解析采用SAX工具,先按照XML文件格式解析,再按照spring bean也有的定义解析,在BeanDefinitionParserDelegate.parseBeanDefinitionElement()实现。
7. 最后对beanDefinition信息进行注册。就是将每个beanDefinition以key  =beanName,value = beanDefinition放入一个hashMap中,在DefaultListenableFactoty.RegisterBeanDefinition()中实现。
经过IoC容器的初始化后,IoC容器持有beanDefintion,为依赖注入bean即调用getBean()方法奠定了基础。
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值