转载

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载入(上)
欢迎关注微信公众号
原文  https://juejin.im/post/5a6f0e8bf265da3e303cc302
正文到此结束
Loading...