glmapper

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

字数统计: 1.9k阅读时长: 8 min
2018/11/10 Share

继上一篇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配置文件,同样会一块拿到路径),如下图所示:

然后继续通过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载入(下)中来说.



欢迎关注微信公众号

原文作者:GuoLei Song

原文链接:http://www.glmapper.com/2018/11/10/spring-series-bd-loaded-1/

发表日期:November 10th 2018, 2:07:03 pm

更新日期:December 14th 2019, 10:54:07 am

版权声明:转载请注明出处

CATALOG