Spring Bean的注册

本文详细介绍了Spring框架中Bean的注册过程,从BeanFactory接口开始,深入探讨了XmlBeanDefinitionReader如何加载Bean定义,以及Bean定义如何被解析并最终注册到Spring容器中。
摘要由CSDN通过智能技术生成

Bean的定义

开始介绍:BeanFactory是最顶层的接口,它定义了IOC容器的基本功能规范,可以看到xml配置中的某些属性了,比方说单例,多例,类型匹配,别名等。

BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。

每个接口都有各自的定义,它主要是为了区分在Spring内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。

  • ListableBeanFactory接口表示这些Bean是可列表的。
  • HierarchicalBeanFactory表示的是这些Bean是有继承关系的,也就是每个Bean有可能有父Bean。
  • AutowireCapableBeanFactory接口定义Bean的自动装配规则。

四个接口共同定义了Bean的集合、Bean之间的关系、以及Bean行为。

从最顶级的BeanFactory开始看起。


  • DefaultListableBeanFactory
  • XmlBeanDefinitionReader

容器的初始化包括BeanDefinition的Resource定位、载入和注册这三个基本的过程。

XmlBeanFactory来举例吧。

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

可以看出非常简单,两个构造方法,一个变量,资源加载的实现是this.reader.loadBeanDefinitions(resource)。

配置文件处理

XmlBeanDefinitionReader加载数据就是在这里,但是这里有一个super(parentBeanFactory);一直往上跟会发现跟到了父类AbstractAutowireCapableBeanFactory。

	public AbstractAutowireCapableBeanFactory() {
		super();
		ignoreDependencyInterface(BeanNameAware.class);
		ignoreDependencyInterface(BeanFactoryAware.class);
		ignoreDependencyInterface(BeanClassLoaderAware.class);
	}

ignoreDependencyInterface:他会自动忽略给定接口的自动装配功能。

Spring自动装配:A依赖B,那么当Spring获取A的bean时后如果属性B未初始化,那么他就会自动初始化B;

有些情况下不能初始化B,比方说B实现了BeanNameAware接口;

  • Spring是这样描述的:自动装配时忽略给定的依赖接口,典型的应用是通过其他方式解析ApplicationContext注册依赖,类似于BeanFactory通过BeanFactoryAware进行注入或者ApplicationContext通过ApplicationContextAware进行注入。

读取Bean配置文件

继续描述this.reader.loadBeanDefinitions(resource)这个是资源载入的入口。

1.XmlBeanDefinitionReader首先对这个Resource对象用EncodedResource进行封装。

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

2.拿着Resource对象获取输入流,并构造inputSource

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

3.通过构造的InputSource和Resource继续调用函数doLoadBeanDefinitions。继续往里面跟进我们可以发现

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

这里的doLoadBeanDefinitions就是核心处理继续跟进可以发现。

try {
	Document doc = doLoadDocument(inputSource, resource);
	return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
	throw ex;
}...

可以看到注册Bean之前做了这些事情,

  1. 获取xml验证模式。
  2. 加载xml并获取一个Document对象,
  3. 然后用这两个对象去注册Bean。

获取XML验证模式:

  • xml的验证模式有DTD(document type definition)和XSD(xml schema definition)。

获取Document:

  • 采用SAX解析XML,如果是dtd类型的文件,解析器直接在当前路径,如果是xsd的默认到META_INF/Spring.schemas文件夹里找XSD文件加载。

解析注册BeanDifinitions:

通过解析配置获取到Bean的定义之后开始注册Bean。


验证xml模式先不看了,继续往下跟进,直接到注册bean的地方。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	// 实例化一个Reader
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	// 统计当前BeanDefinition的个数
	int countBefore = getRegistry().getBeanDefinitionCount();
	// 加载注册Bean
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	// 返回本次注册Bean的个数
	return getRegistry().getBeanDefinitionCount() - countBefore;
}

可以看到registerBeanDefinitions这里就是加载注册Bean的地方,继续跟进下去。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	logger.debug("Loading bean definitions");
	Element root = doc.getDocumentElement();
	doRegisterBeanDefinitions(root);
}

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

但是继续跟进去就发现很奇怪的现象。

protected void preProcessXml(Element root) {
}
protected void postProcessXml(Element root) {
}
这里用到了模板方法模式。他是为子类所设计的方法,在Bean解析前后做一些处理,只需要继承这个父类,子类自己去重写就好了。

我们继续往里面跟进。

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

到这里就发现一种是自定义命名空间的处理,一种是默认的处理。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recurse
		doRegisterBeanDefinitions(ele);
	}
}

我们走倒数第二个就是最普通的Bean id这种方式装配


接下来进去发现最后容器是一个并发容器ConcurrentHashMap,这样就把Bean放到了Spring容器中了,这就完成了Spring的Bean的注册。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值