前言:
Spring容器有多种实现方式,一般来说可分为:BeanFactory和ApplicationContext
* BeanFactory提供了容器的基本功能,如getBean()等功能
* ApplicationContext接口继承了BeanFactory,不但实现了BeanFactory的所有功能,还对其进行了扩展。
扩展功能如下:1)MessageSource,提供国际化的消息访问;2)资源访问,如URL和文件;3)事件传播特性,即支持AOP特性;4)载入多个有继承关系的上下文,使得每一个上下文都专注与一个特定的层次,比如应用的Web层
本文则基于BeanFactory接口的实现类XMLBeanFactory来介绍其加载xml文件的过程
笔者使用SpringBoot来进行开发,spring-boot-start-parent版本为1.5.3.RELEASE,所依赖的Spring组件(如context、core)等版本为4.3.8.RELEASE
1.XMLBeanFactory的基本使用
1)创建实体类Student
@Data
public class Student {
private int id;
private String name;
}
2)创建文件beans.xml,具体内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="test.Student"/>
</beans>
3)测试方法
public class Test {
public static void main(String[] args) {
XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("beans.xml"));
Student bean = (Student) bf.getBean("student");
System.out.println(bean); //结果为:Student(id=0, name=null)
}
}
以上便是通过加载xml的方式来获取bean
2.写在分析XmlBeanFactory源码之前
在查看XmlBeanFactory实现其相关功能的源码以前,我们可以大胆猜想一下,如果是我们自己,那应该如何来实现这个功能?
如果是我的话,最简单的思路就是:
1)先使用一个解析工具(一般来说就是DOM解析或者SAX解析)来解析beans.xml,获取其内容(一般解析完成之后都是获取一个Document)
2)解析该Document,组装bean的基本信息,如name、class等信息,将这些信息放到一个bean的实体之中
3)将组装完的bean信息,放到一个map中,name作为key,class对应的实例作为value,这样用户就可以通过getBean等方法来获取该bean
大胆设想这种实现BeanFactory的方式,总体看来这样实现简单易用也是不错的,读者也可以自己设想下其实现方式。
3.XmlBeanFactory源码架构分析
下面跟随笔者先大概看一下XmlBeanFactory加载bean.xml的大概架构
1)new XmlBeanFactory(Resource resource)构造XmlBeanFactory
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);// 自定义的Reader
public XmlBeanFactory(Resource resource) throws BeansException {// 默认构造方式
this(resource, null);// 调用下一个构造方法,parentBeanFactory默认为null
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource); // 主要功能实现
}
2)reader.loadBeanDefinitions(Resource resource) 解析器加载resource
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
...
try {
// 1.从resource中获取流信息
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 2.加载流信息
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
...
}
3)doLoadBeanDefinitions(...)加载流信息
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 1.通过xml解析inputStream来获取xml对应的Document
Document doc = doLoadDocument(inputSource, resource);
// 2.解析Document,注册其中的bean
return registerBeanDefinitions(doc, resource);
}
...
}
总结:通过以上对XmlBeanFactory结构的分析,可知,其主要功能也是按照我们的猜想来进行的
主要分为三个步骤:
* 将beans.xml加载为流信息
* 解析该流信息,将其解析为一个Document
* 加载该Document,注册其中的bean(到某一个地方),该步骤也是最重要的步骤
下面按照这三个步骤,逐步解析XmlBeanFactory功能
4.将beans.xml加载为流信息
由XMLBeanFactory的构造方法可知,构造器的入参为Resource,下面看一个Resource的主要方法:
/**
主要方法:
* @see #getInputStream()
* @see #getURL()
* @see #getURI()
* @see #getFile()
主要实现:
* @see WritableResource
* @see ContextResource
* @see UrlResource
* @see ClassPathResource
* @see FileSystemResource
* @see PathResource
* @see ByteArrayResource
* @see InputStreamResource
*/
public interface Resource extends InputStreamSource {}
主要实现方式有以上几种,通常我们使用的就是ClassPathResource(将配置文件放到src/main/resources中),或者FileSystemResource(将配置文件放到系统盘某个路径下)
在XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)中可知
InputStream inputStream = encodedResource.getResource().getInputStream();
InputStream的获取是通过Resource.getInputStream()方法来实现的,下面来查看一下ClassPathResource.getInputStream()方法:
@Override
public InputStream getInputStream() throws IOException {
InputStream is;
// 默认情况下clazz为null
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
// classLoader不为空,默认为ClassUtils.getDefaultClassLoader(),也就是Thread.currentThread().getContextClassLoader()
else if (this.classLoader != null) {
// 所以通过classLoader来加载path资源
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
总结:由上可知,bean.xml转换为流信息,主要是通过classLoader.getResourceAsStream()方法来实现的
5.解析InputStream,将其解析为一个Document
解析流为Document的主要代码为XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 1.通过xml解析inputStream来获取xml对应的Document
Document doc = doLoadDocument(inputSource, resource);
...
}
...
}
下面来解析这个doLoadDocument方法
1)doLoadDocument()
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
2)loadDocument()
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
// 1.创建DocumentBuilder,直接从DocumentBuilderFactory中获取,通过factory.newDocumentBuilder()方法
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
// 2.使用DocumentBuilder来解析流信息
return builder.parse(inputSource);
}
3)build.parse()
public Document parse(InputSource is) throws SAXException, IOException {
if (is == null) {
throw new IllegalArgumentException(
DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
"jaxp-null-input-source", null));
}
if (fSchemaValidator != null) {
if (fSchemaValidationManager != null) {
fSchemaValidationManager.reset();
fUnparsedEntityHandler.reset();
}
resetSchemaValidator();
}
// 通过DOM解析,来获取Document
domParser.parse(is);
Document doc = domParser.getDocument();
domParser.dropDocumentReferences();
return doc;
}
总结:由上文可知,Spring使用了DOM解析的方式来解析InputStream,最终获取Document
6.加载该Document,注册其中的bean(到某一个地方)
下面来看XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法的下半段
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
...
// 2.解析Document,注册其中的bean
return registerBeanDefinitions(doc, resource);
}
...
}
1)XmlBeanDefinitionReader.registerBeanDefinitions()
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 1.创建BeanDefinitionDocumentReader,BeanDefinitionDocumentReader接口默认实现为DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 2.注册Document中的元素
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
2)DefaultBeanDefinitionDocumentReader.registerBeanDefinitions()
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
// 1.获取rootElement
Element root = doc.getDocumentElement();
// 2.注册
doRegisterBeanDefinitions(root);
}
// doRegisterBeanDefinitions()
protected void doRegisterBeanDefinitions(Element root) {
// 前置处理(暂时为空,用户可自定义实现)
preProcessXml(root);
// 真正的解析处理
parseBeanDefinitions(root, this.delegate);
// 后置处理(暂时为空,用户可自定义实现)
postProcessXml(root);
this.delegate = parent;
}
// parseBeanDefinitions()
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
// 1.获取所有的子节点
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)) {
// 2.使用默认实现(本例中没有自定义标签,所以会使用该默认实现)
parseDefaultElement(ele, delegate);
}
else {
// 用户自定义实现
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
// parseDefaultElement()
// 根据不同的nodeName,对应不同的解析方案,暂时只分析最重要的一个BEAN_ELEMENT的解析
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);
}
}
3)DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)最重要的方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 1.BeanDefinitionHolder用于全方位的来描述一个bean信息,包括name/class/alias/等一系列属性,将element解析为BeanDefinitionHolder
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 2.对其属性进行装饰
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 3.将bean实例注册到(某一个地方)
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 4.发送注册完成事件给相应的监听器
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
4)BeanDefinitionParserDelegate.parseBeanDefinitionElement()
将Document中的Element解析为一个BeanDefinitionHolder,BeanDefinitionHolder为一个bean信息的综合描述
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
// parseBeanDefinitionElement()
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 1.获取id 和 name属性值
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
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");
}
}
// 2.判断beanName是否重复,如果已有该beanName,则报错
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 3.解析Element元素(重要方法)
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 4.如果用户没有写id,则自动生成一个beanName
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
...
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 5.组装为BeanDefinitionHolder
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
// parseBeanDefinitionElement()
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// 1.获取class属性值
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
// 2.创建AbstractBeanDefinition,AbstractBeanDefinition为bean的描述信息类(重要方法)
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 3.下面的parse方法均为对AbstractBeanDefinition中属性的补充
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
...
return null;
}
// createBeanDefinition()
protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
// createBeanDefinition()
public static AbstractBeanDefinition createBeanDefinition(
String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
// 由className反射获取BeanClass
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
总结:整个4)方法,总体来说就是为了获取对bean的完整描述信息,描述信息都存放在BeanDefinitionHolder类中;BeanClass信息由反射的方式来获取
5)将BeanDefinitionHolder实例注册到(某一个地方)
4)方法已经将Element元素解析为一个完整的BeanDefinitionHolder类,里面包含了Element元素所有的基本信息,下面就看下,如何注册
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
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);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
// registerBeanDefinition()
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 1.获取beanName
String beanName = definitionHolder.getBeanName();
// 2.进行注册
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
...
}
// DefaultListableBeanFactory.registerBeanDefinition(),默认实现为DefaultListableBeanFactory
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
...
BeanDefinition oldBeanDefinition;
// 1.从当前的map中获取,是否已有该bean,如果已有该bean,则重新覆盖
// map为:private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
...
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 2.当前bean第一次加载
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 3.同步的情况下,将该bean放入map中
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
...
}
总结5):由以上分析可知,注册行为就是将 beanName和beanDefinition 放入到DefaultListableBeanFactory的beanDefinitionMap中去
总结:
1)由6整个步骤可知:在获取Document之后,注册器所做的就是依次解析所有的Element,将Element解析为一个BeanDefinitionHolder,最后将beanName和对应的BeanDefinitionHolder放入到一个ConcurrentHashMap中去
2)到此为止,XMLBeanFactory的构造方法解析完毕,总结下其过程为:
* 将beans.xml文件读到内存,包装为InputStream
* DOM解析的方式来解析InputStream,最后生成一个Document
* 注册器解析Document,解析出Document的每一个Element
* 将Element解析为一个BeanDefinitionHolder
* 最后将beanName对应的BeanDefinitionHolder放入ConcurrentHashMap中,完成注册步骤