Spring源码系列:BeanDefinition载入(上)

栏目: Java · 发布时间: 6年前

内容简介:Spring源码系列:BeanDefinition载入(上)

继上一篇BeanFactory的创建之后,其实就是BeanDefinition载入了。同样也是在AbstractRefreshableApplicationContext类的refreshBeanFactory方法中完成:

//创建默认的DefaultListableBeanFactory工厂
DefaultListableBeanFactory beanFactory = createBeanFactory();
//设置Id
beanFactory.setSerializationId(getId());
//这个方法其实就是设置了allowBeanDefinitionOverriding和allowCircularReferences两个属性
customizeBeanFactory(beanFactory);

//调用子类的加载bean定义方法,这里会调用XmlWebApplicationContext子类的复写方法
loadBeanDefinitions(beanFactory);

这里的loadBeanDefinitions也是一个抽象方法,AbstractRefreshableApplicationContext类中并没有给出具体的实现,二是通过子类XmlWebApplicationContext的loadBeanDefinitions完成具体实现。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 
        throws BeansException, IOException {
	//创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
	//为XmlBeanDefinitionReader配置Environment
	beanDefinitionReader.setEnvironment(getEnvironment());
	//为XmlBeanDefinitionReader配置ResourceLoader,
	//因为DefaultResourceLoader是父类,所以this可以直接被使用
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

	// 允许子类提供reader的自定义初始化,然后继续实际加载bean定义。
	initBeanDefinitionReader(beanDefinitionReader);
	loadBeanDefinitions(beanDefinitionReader);
}

initBeanDefinitionReader初始化用于加载此上下文的bean定义的bean定义读取器;默认实现是空的。然后下面通过重载的loadBeanDefinitions来做具体的bean解析(这里用到的是XmlBeanDefinitionReader这个解析器);使用给定的XmlBeanDefinitionReader加载bean definitions。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
	String[] configLocations = getConfigLocations();
	//遍历xml文件
	if (configLocations != null) {
		for (String configLocation : configLocations) {
			reader.loadBeanDefinitions(configLocation);
		}
	}
}

此时会将我们的applicationContext.xml读入(当然如何还有其他的spring配置文件,同样会一块拿到路径),如下图所示:

Spring源码系列:BeanDefinition载入(上)

然后继续通过loadBeanDefinitions的重载方法继续委托调用。最后交给AbstractBeanDefinitionReader的loadBeanDefinitions来完成;这个代码比较长,拆开一步一步来说,先看下整体的:

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
    //获取ResourceLoader资源定位器
	ResourceLoader resourceLoader = getResourceLoader();
	//如果定位器为null,则抛出异常
	if (resourceLoader == null) {
		throw new BeanDefinitionStoreException(
				"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
	}
    //是否是ResourcePatternResolver类型的定位器
	if (resourceLoader instanceof ResourcePatternResolver) {
		// Resource pattern matching available.
		try {
			Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
			int loadCount = loadBeanDefinitions(resources);
			if (actualResources != null) {
				for (Resource resource : resources) {
					actualResources.add(resource);
				}
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
			}
			return loadCount;
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"Could not resolve bean definition resource pattern [" + location + "]", ex);
		}
	}
	//非ResourcePatternResolver类型的
	else {
		// Can only load single resources by absolute URL.
		Resource resource = resourceLoader.getResource(location);
		int loadCount = loadBeanDefinitions(resource);
		if (actualResources != null) {
			actualResources.add(resource);
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
		}
		return loadCount;
	}
}

上面的代码中需要说明下为什么要判断当前resourceLoader是否是ResourcePatternResolver类型的,因为ResourceLoader只是提供了对classpath前缀的支持。而ResourcePatternResolver提供了对classpath*前缀的支持。也就是说ResourceLoader提供classpath下单资源文件的载入,而ResourcePatternResolver提供多资源文件的载入。 先看下假如是ResourcePatternResolver类型的(略去了部分log代码):

try {
    //先得到我们的resources
	Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
	//解析并返回beanDefinition的数量
	int loadCount = loadBeanDefinitions(resources);
	//加载过程中已经被解析过的实际的Resource的填充集合
	if (actualResources != null) {
		for (Resource resource : resources) {
			actualResources.add(resource);
		}
	}
	return loadCount;
}
catch (IOException ex) {
	throw new BeanDefinitionStoreException(
		"Could not resolve bean definition resource pattern [" + location + "]", ex);
}

非ResourcePatternResolver类型情况:

// Can only load single resources by absolute URL.
//只能通过绝对URL加载单个资源
Resource resource = resourceLoader.getResource(location);
//解析并返回beanDefinition的数量
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
	actualResources.add(resource);
}
return loadCount;

然后继续通过重载方法loadBeanDefinitions(Resource... resources)来解析(AbstractBeanDefinitionReader类中)

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
	Assert.notNull(resources, "Resource array must not be null");
	//初始化beanDefiniton个数
	int counter = 0;
	//遍历当前资源数组
	for (Resource resource : resources) {
	    //解析具体resource中的bean
		counter += loadBeanDefinitions(resource);
	}
	return counter;
}

然后交给子类XmlBeanDefinitionReader中的loadBeanDefinitions(Resource resource)方法:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(new EncodedResource(resource));
}

继续通过重载方法loadBeanDefinitions(EncodedResource encodedResource)执行,这个方法我们只关注最核心的代码:

//获取输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
    //资源读取inputSource
	InputSource inputSource = new InputSource(inputStream);
	if (encodedResource.getEncoding() != null) {
		inputSource.setEncoding(encodedResource.getEncoding());
	}
	//委托给doLoadBeanDefinitions来完成
	return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
	inputStream.close();
}

doLoadBeanDefinitions是XmlBeanDefinitionReader中的方法,来看核心代码:

//解析成符合w3c标准的Document
Document doc = doLoadDocument(inputSource, resource);
//继续交给registerBeanDefinitions来处理
return registerBeanDefinitions(doc, resource);

这个时候已经将loadBeanDefinitions换成registerBeanDefinitions了,也就是载入并注册;registerBeanDefinitions同样也是XmlBeanDefinitionReader中的方法:

public int registerBeanDefinitions(Document doc, Resource resource) throws
BeanDefinitionStoreException {
    //得到documentReader用来读取document文档
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	//注册之前的bean个数
	int countBefore = getRegistry().getBeanDefinitionCount();
	//解析并注册bean
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	return getRegistry().getBeanDefinitionCount() - countBefore;
}

仍然没有处理,继续交给 BeanDefinitionDocumentReader 的registerBeanDefinitions方法来完成:

//这个实现根据“spring-beans”XSD(或DTD)解析bean定义。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	logger.debug("Loading bean definitions");
	Element root = doc.getDocumentElement();
	doRegisterBeanDefinitions(root);
}

还是没处理,又细化一步,交给 DefaultBeanDefinitionDocumentReader 的doRegisterBeanDefinitions(Element root)方法:

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.isInfoEnabled()) {
    				logger.info("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;
}

任何嵌套的<beans>元素都将导致此方法的递归。 为了正确传播和保存<beans> default- *属性,请跟踪当前(父)委托,该委托可能为null。 为了回退的目的,创建一个引用父对象的新(子)委托,然后最终重置this.delegate回到它的原始(父)引用。这个行为模仿了一堆委托,而实际上并不需要一个委托。(翻译的有点蹩脚,大概意思就是这)

所以说 DefaultBeanDefinitionDocumentReader 自己也没干这事,又给了 BeanDefinitionParserDelegate ,然后就是preProcessXml()、parseBeanDefinitions()、postProcessXml()方法;其中preProcessXml()和postProcessXml()默认是空方法,自己没有实现。具体解析在parseBeanDefinitions(root, this.delegate)中完成。

BeanDefinitionParserDelegate 用于将 Document 的内容转成 BeanDefinition 实例; BeanDefinitionDocumentReader 本身不具备该功能而是交给了该类来完成。

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

这个方法就是解析文档中根目录下的元素:“import”,“alias”,“bean”。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    //解析一个“import”元素,并将给定资源的bean定义加载到bean工厂中。
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		importBeanDefinitionResource(ele);
	}
	//处理给定的别名元素,向注册表注册别名。
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		processAliasRegistration(ele);
	}
	//处理给定的bean元素,解析bean定义并将其注册到注册表中。
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
	//在给定的根<beans />元素内注册每个bean定义。
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recurse
		doRegisterBeanDefinitions(ele);
	}
}

先来看 processBeanDefinition 这个方法;

BeanDefinitionHolder是一个BeanDefinition的持有者,其定义了一下变量,并对以下变量提供get和set操作。这个在后面的说道BeanDefinition体系的时候再聊。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    //获取一个BeanDefinitionHolder
	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);
		}
		// 发送注册事件。
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

接着看 registerBeanDefinition 这个方法:通过给定的bean工厂注册给定的bean definition 。

public static void registerBeanDefinition(
	BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
	throws BeanDefinitionStoreException {

	// 在主名称下注册bean定义。
	String beanName = definitionHolder.getBeanName();
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

	// 如果有的话,注册bean名称的别名,
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias : aliases) {
			registry.registerAlias(beanName, alias);
		}
	}
}

registerBeanDefinition 里面又通过调用 BeanDefinitionRegistry 接口的实现 DefaultListableBeanFactory 来完成具体的注册过程。关于 DefaultListableBeanFactoryregisterBeanDefinition 方法的解析逻辑将在 Spring源码系列:BeanDefinition载入(下) 中来说.

Spring源码系列:BeanDefinition载入(上)
欢迎关注微信公众号

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

About Face 3

About Face 3

Alan Cooper、Robert Reimann、David Cronin / John Wiley & Sons / 2007-5-15 / GBP 28.99

* The return of the authoritative bestseller includes all new content relevant to the popularization of how About Face maintains its relevance to new Web technologies such as AJAX and mobile platforms......一起来看看 《About Face 3》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具