spring 源码解析第一章之xml文件解析转换bean,注册流程的实现

3 篇文章 0 订阅

spring 系列

1.spring 是一个一个引擎,
2.Spring MVC 是基于 Spring 的一个 MVC 框架
3.Spring Boot 是基于 Spring4 的条件注册的一套快速开发整合包

1.spring 源码解读:

我们来理一下其中的关系:
core container(核心容器):包含了 beans ,core,context, expression language模块,
core 和 bean 是spring的基础部分,提供IOC和 DI 特性,实现是用工厂设计模式实现的,beanFactory,

Spring 的核心之IOC:
IOC (Inversion of Control): 控制反转,把对象本身的控制权转向Spring容器,从而由Spring来管理对象的生命周期。

IOC 大家都知道,应用也很广泛,因为java里面都是各种各样的bean ,ioc 刚好是来管理bean的,而 Ioc 的实现原理和具体实现你可能不是很清楚,那我们来看一下

核心类:
DefaultListableBeanFactory,这个类就是ioc实现的核心类了,所以重要的操作都在,

他继承了 AbstractAutowireCapableBeanFactory 并且实现了两个接口类ConfigurableListableBeanFactory,BeanDefinitionRegistry,并且序列化了。下面是相关类的组成结构。

首先我们来看一下

1.配置文件封装

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(“BizAccountDao.xml”));
具体操作步骤是这样的
,首先我用ClassPathResource的构造函数来构造Resource资源文件的实例对象,因为XmlBeanFactory的构造函数需要Resource的实例,后面的操作跟这个Resource实例都是连着的,有了Resource对象就可以进行xmlbeanfactory的初始化了

那么 Resource 是如何封装的呢? 找到Resource这个类看一下,这个类继承了 InputStreamSource接口类, InputStreamSource 封装任何能返回 InputStream的类,例如file,classpath的资源文件,和byte ,Array之类的,
Resource 抽象了需要使用到的底层资源:file,url等,还有有对文件的相关操作接口,我们传入的参数十一个url的字符串地址,原来他是根据我们传入的路径来查找对应的文件的,
public interface Resource extends InputStreamSource {

/**
*文件是否存在
**/
    boolean exists();
/**
*文件是否可读
**/
    boolean isReadable();
/**
*文件是否打开状态
**/
    boolean isOpen();
/**
*得到url对象
**/
    URL getURL() throws IOException;
/**
*得到uri对象
**/
    URI getURI() throws IOException;
/**
*得到file对象
**/
    File getFile() throws IOException;
/**
*获取对象的内容长度
**/
    long contentLength() throws IOException;
/**
*
**/
    long lastModified() throws IOException;
/**
*基于当前资源 创建一个Resource 资源对象
**/
    Resource createRelative(String var1) throws IOException;
/**
*获取Resource文件的名称
**/
    String getFilename();
/**
*打印错误信息
**/
    String getDescription();
}

public interface InputStreamSource {
/**
*得到 InputStream 对象
**/
    InputStream getInputStream() throws IOException;
}

那我们在整理一下逻辑; 如下图
首先我们需要一个resource对象,用classpathresource来创建resource对象,创建resource对象之后我们可以使用resource提供的功能对resource操作,resource实例创建好了之后,把他传入到xmlbeanfactory的构造函数中,由xmlbeanfactory进行初始化。


@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {
    private final XmlBeanDefinitionReader reader;

    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, (BeanFactory)null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        //初始化  resource
        super(parentBeanFactory);
        this.reader = new XmlBeanDefinitionReader(this);
         //加载
        this.reader.loadBeanDefinitions(resource);
    }
}

接下来xmlbeanfactory做的事情就是把xml文件转bean并初始化的一个过程,resource传入后构造函数之后,xmlbeanfactory又调用了另一个构造函数, super(parentBeanFactory); 他调用了父类的构造函数对beanfactory进行初始化, 下面是他的初始化过程。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {
//调用AbstractAutowireCapableBeanFactory 父类中的构造函数(2)
    super(parentBeanFactory);
}
(2)
public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {
  //初始化(3)
    this();
  //实例化 (AbstractBeanFactory父类4)
    this.setParentBeanFactory(parentBeanFactory);
}
//3
public AbstractAutowireCapableBeanFactory() {
    this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
    this.parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    this.allowCircularReferences = true;
    this.allowRawInjectionDespiteWrapping = false;
    this.ignoredDependencyTypes = new HashSet();
    this.ignoredDependencyInterfaces = new HashSet();
    this.factoryBeanInstanceCache = new ConcurrentHashMap(16);
    this.filteredPropertyDescriptorsCache = new ConcurrentHashMap(256);
    this.ignoreDependencyInterface(BeanNameAware.class);
    this.ignoreDependencyInterface(BeanFactoryAware.class);
    this.ignoreDependencyInterface(BeanClassLoaderAware.class);
}
//4
public void setParentBeanFactory(BeanFactory parentBeanFactory) {
    if (this.parentBeanFactory != null && this.parentBeanFactory != parentBeanFactory) {
        throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory);
    } else {
        this.parentBeanFactory = parentBeanFactory;
    }
}
}

最后关键部分道到了,上面初始化的前置工作完成之后,Wimbledon就要真正实现资源的加载了,如下:

//XmlBeanFactory类
this.reader.loadBeanDefinitions(resource);
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//再次调用构造方法
    return this.loadBeanDefinitions(new EncodedResource(resource));
}

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
    }

    Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    if (!((Set)currentResources).add(encodedResource)) {
        throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    } else {
        int var5;
        try {
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }

                var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            } finally {
                inputStream.close();
            }
        } catch (IOException var15) {
            throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
        } finally {
            ((Set)currentResources).remove(encodedResource);
            if (((Set)currentResources).isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
        return var5;
    }
}
public final Resource getResource() {
    return this.resource;
}

这里我们提一下一个重要的东西,

为什么要提到他呢,那肯定是他的作用啦,作用:忽略给定接口的自动装配,什么意思呢?例如:有两个类A,B,A中有属性B,那么spring获取A的bean时,如果其属性B还有初始化,那么spring会自动初始化B,这也是spring提供的一个重要的特性,but,也有不会自动初始化的情况,
例如B 实现了c接口,而spring是这样解释的:自动装配时忽略给定的依赖接口。我们在接口和接口实现类中就有应用到,下面举个例子:

public interface Aservice{
public String get();
}
public interface AserviceImpl{
public String get(){
retrun "A";
}
}
//使用
public class Acontroller{
@Autowired
Aservice aservice;

public Map get(){
Map<String,Object> map = new HashMap();
map.put("A",aservice.get());
retrun map;
}
}

这是典型的通过解析Application上下文注册依赖,进行对应的注入。

2.加载
封装完成后就需要加载他了,那么什么时候才是加载他呢?我们来看一下

public class XmlBeanFactory extends DefaultListableBeanFactory {
    private final XmlBeanDefinitionReader reader;

    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, (BeanFactory)null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader = new XmlBeanDefinitionReader(this);
        this.reader.loadBeanDefinitions(resource);
    }
}
this.reader.loadBeanDefinitions(resource);实现真正的加载操作,
1.
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//对resource对象进行封装,再调用另一个函数来处理接下里的事情(2)
    return this.loadBeanDefinitions(new EncodedResource(resource));
}
2.
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
    }
    Set<EncodedResource> currentResources =(Set)this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    if (!((Set)currentResources).add(encodedResource)) {
        throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    } else {
        int var5;
        try {
//获取输入流,构造inputsource
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                //实例化输入流
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                  //对resource进行编码
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
//获取document (核心)
                var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            } finally {
                inputStream.close();
            }
        } catch (IOException var15) {
            throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
            ((Set)currentResources).remove(encodedResource);
            if (((Set)currentResources).isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
        return var5;
    }
}

根据上面的函数我们可以看到他的加载过程,
看到这里是不是觉得前面讲好多没必要的东西!那我现在在屡一下逻辑
1.首先我们需要一个resource参数做封装
2.对resource进行编码处理
3.通过sax读取xml文件凡是来准备inputsource对象
4.把前面准备好的inputsource出入核心函数doLoadBeanDefinitions中,

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    try {
//1.加载xml文件,得到对应的document对象
        Document doc = this.doLoadDocument(inputSource, resource);
//2.根据document注册bean信息
        return this.registerBeanDefinitions(doc, resource);
//3.下面异常是验证xml的格式是否正确
    } catch (BeanDefinitionStoreException var4) {
        throw var4;
    } catch (SAXParseException var5) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
    } catch (SAXException var6) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
    } catch (ParserConfigurationException var7) {
        throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
    } catch (IOException var8) {
        throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
    } catch (Throwable var9) {
        throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
    }
}

上面的三个步骤都很重要,那么这里又引申出一个重要的点,那就是xml的验证。
2.1 xml的验证码模式
xml格式分为两种 DTD 和 XSD
DTD(document type definition)文档类型定义:xml月数模式语言的一种,是xml文件的验证机制,可通过比较xml文档和DTD文件;哎看文件是否符合规范,元素和标签是否正确,一个DTD文档包含元素的定义规则,元素减关系的定义规则,元素的可使用性,可使用的实体或符号规则,
例如:在document标签中声明为.dtd,

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huimi.core.business.dao.BizAccountDao">
<resultMap type="com.huimi.core.business.entity.BizAccount" id="BizAccountMap">
    <id property="id" column="id" jdbcType="INTEGER"/>
    <result property="uuid" column="uuid" jdbcType="VARCHAR"/>
    <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
</mapper>

2.2 XSD(XML schemas definition)可描述 XML 文档的结构:
什么是xml schema:XML Schema 的作用是定义 XML 文档的合法构建模块,类似 DTD。
定义可出现在文档中的元素
定义可出现在文档中的属性
定义哪个元素是子元素
定义子元素的次序
定义子元素的数目
定义元素是否为空,或者是否可包含文本
定义元素和属性的数据类型
定义元素和属性的默认值以及固定值
xml schema描述了xml文档的结构。可以用指定的xml schema来验证某个xml文档,已检查xml文档是否符合其要求,xml schema 本身是一个xml 文档,他符合xml语法结构,可以用通用的xml 解析器来解析它。
在使用xml schema文档对xml实例进行校验,处理要声明空间外,还需要指定该名称空间所对应的存储位置,通过 schemalocation属性来指定名称空间所对应的xml schema文档的存储位置,他包含两部分,
1.命名空间的URI,
2.名称空间所标识的xml schema文件位置或者URL地址(xsi:noNamespaceSchemaLocation=“shiporder.xsd”)

代码如下:

<?xml version="1.0" encoding="ISO-8859-1"?>

<shiporder orderid="889923"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="shiporder.xsd">
 <orderperson>George Bush</orderperson>
 <shipto>
  <name>John Adams</name>
  <address>Oxford Street</address>
  <city>London</city>
  <country>UK</country>
 </shipto>
 <item>
  <title>Empire Burlesque</title>
  <note>Special Edition</note>
  <quantity>1</quantity>
  <price>10.90</price>
 </item>
 <item>
  <title>Hide your heart</title>
  <quantity>1</quantity>
  <price>9.90</price>
 </item>
</shiporder>

了解了DTD和XSD的区别之后我们再去分析Spring中对验证模式的提取就稍微容易理解一点了,通过之前的分析我们往下找了一下,找到了getValidationModeForResourcef函数,这个函数是spring获取对应资源的验证模式,

    return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
}

protected int getValidationModeForResource(Resource resource) {
//默认等于1
    int validationModeToUse = this.getValidationMode();
//手动设置指定凡是验证模式
    if (validationModeToUse != 1) {
        return validationModeToUse;
    } else {
//默认自动检测
        int detectedMode = this.detectValidationMode(resource);
        return detectedMode != 1 ? detectedMode : 3;
    }
}
//自动检测
protected int detectValidationMode(Resource resource) {
    if (resource.isOpen()) {
        throw new BeanDefinitionStoreException("Passed-in Resource [" + resource + "] contains an open stream: cannot determine validation mode automatically. Either pass in a Resource that is able to create fresh streams, or explicitly specify the validationMode on your XmlBeanDefinitionReader instance.");
    } else {
        InputStream inputStream;
        try {
            inputStream = resource.getInputStream();
        } catch (IOException var5) {
            throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: cannot open InputStream. Did you attempt to load directly from a SAX InputSource without specifying the validationMode on your XmlBeanDefinitionReader instance?", var5);
        }
        try {
            return this.validationModeDetector.detectValidationMode(inputStream);
        } catch (IOException var4) {
            throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", var4);
        }
    }
}
自动检测的流程是调用detectValidationMode函数,detectValidationMode函数中获取resource的输入流之后出入到XmlValidationModeDetector类的detectValidationMode函数中,代码如下:
protected int detectValidationMode(Resource resource) {
    if (resource.isOpen()) {
        throw new BeanDefinitionStoreException("Passed-in Resource [" + resource + "] contains an open stream: cannot determine validation mode automatically. Either pass in a Resource that is able to create fresh streams, or explicitly specify the validationMode on your XmlBeanDefinitionReader instance.");
    } else {
        InputStream inputStream;
        try {
            inputStream = resource.getInputStream();
        } catch (IOException var5) {
            throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: cannot open InputStream. Did you attempt to load directly from a SAX InputSource without specifying the validationMode on your XmlBeanDefinitionReader instance?", var5);
        }

        try {
            return this.validationModeDetector.detectValidationMode(inputStream);
        } catch (IOException var4) {
            throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", var4);
        }
    }
}

XmlValidationModeDetector类

    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    byte var4;
    try {
        boolean isDtdValidated = false;
        while(true) {
            String content;
            if ((content = reader.readLine()) != null) {
                content = this.consumeCommentTokens(content);
                //跳过注释或者空字段内容
                if (this.inComment || !StringUtils.hasText(content)) {
                    continue;
                }
                if (this.hasDoctype(content)) {
                    isDtdValidated = true;
                //读取到内容以<符号开始 ,验证模式一定会在开始符号之前
                } else if (!this.hasOpeningTag(content)) {
                    continue;
                } }
            int var5 = isDtdValidated ? 2 : 3;
            return var5;
        }
    } catch (CharConversionException var9) {
        var4 = 1;
    } finally {
        reader.close();
    }
    return var4;
}

private boolean hasDoctype(String content) {
    return content.contains("DOCTYPE");
}

private boolean hasOpeningTag(String content) {
    if (this.inComment) {
        return false;
    } else {
        int openTagIndex = content.indexOf(60);
        return openTagIndex > -1 && content.length() > openTagIndex + 1 && Character.isLetter(content.charAt(openTagIndex + 1));
    }
}
这个方法是来验证码xml的格式是xsd还是DTD的,没其他的内容了。

验证模式准备好了就可以加载document了,下面的loadDocument就是加载document了,

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
}
-------------------------------------------------------------------
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isDebugEnabled()) {
        logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); }
    DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource);
}

下面就是实现的具体代码了,他用sax方法去解析xml,先创建一个documentbuilderfactory,在通过documentbuilderfactory创建documentbuilder,在对inputsource解析后返回document对象,这里需要提一下参数this.getEntityResolver(),
他解释说:如果sax应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setentityresolver方法先sax驱动注册一个实例。意思是说对于解析一个xml,sax首先读取该xml文档删的声明,根据声明去寻找想要的DTD定义,一百年对文档进行一个验证,默认的寻找规则,通过网络来下载相应的DTD声明,并进行认证,下载的过程很漫长,而且网络不能中断,这里可能会抛异常,就是因为DTD没有找到声明的原因。但是我们可以吧DTD的声明放在项目的某一个地方,这样就避免了去查找声明了。
这个类中有一个抽象方法,代码如下:

    public abstract InputSource resolveEntity (String publicId,
                                              String systemId)
        throws SAXException, IOException;
}

他这里接收了两个参数 publicId,systemId,这两个参数就是来区分xsd和dtd文件的

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.springframework.org/schema/beans/spring-beans.xsd">

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
例1:xsd文件
publicId:null
publicId:http://www.springframework.org/schema/beans/spring-beans.xsd
例2:dtd文件
publicId:null
publicId:http://mybatis.org/dtd/mybatis-3-mapper.dtd

接下来我们要做的就是解析和注册了,上面我们吧文件转换成了document对象后,需要提取document和在spring中注册。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//实例化BeanDefinitionDocumentReader
    BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
//设置环境变量
    int countBefore = this.getRegistry().getBeanDefinitionCount();
//加载注册bean
    documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
//记录本次加载的BeanDefinition个数
    return this.getRegistry().getBeanDefinitionCount() - countBefore;
}

这里的Document doc参数是前面加载转换出来的document对象。而他真正的处理是交给BeanDefinitionDocumentReader类,类中有个接口,具体实现是在DefaultBeanDefinitionDocumentReader类中,代码如下:

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    this.logger.debug("Loading bean definitions");
    Element root = doc.getDocumentElement();
//注册操作(核心)
    this.doRegisterBeanDefinitions(root);
}

//底层实现代码
protected void doRegisterBeanDefinitions(Element root) {
//处理profile属性
    BeanDefinitionParserDelegate parent = this.delegate;
//处理解析
    this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
    if (this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute("profile");
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
            if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                }
                return;
            }
        }
    }
//解析前处理,方法没有做实现,具体实现留在子类中
    this.preProcessXml(root);
    this.parseBeanDefinitions(root, this.delegate);
//解析后处理,方法没有做实现,具体实现留在子类中
    this.postProcessXml(root);
    this.delegate = parent;
}

根据上面的代码我们可以看到他的实现过程,但是有两处的方法都是空的,这就很有意思了,都说不写无用的代码,每句代码都是有他存在的意义,那么他存在肯定是有意义的,那么为什么方法中没有具体实现呢,这个就要提一下java的的一个点了,面向对象,面向继承,如果子类继承了他,只要重写他的方法,就可以根据当前的需求做具体的实现了。上面代码中是不是发现一个疑问点,那就是prolife(侧面描述)变量,profile,这个属性是用来提取xml配置的profile属性的值,根据这个值去加载对应的配置文件,这个在web集成部署的时候方便切换开发和生产环境,我现在开发项目的时候基本是这样的,这样真的很方便。处理了profile户就可以进行xml读取了,跟踪代码进入~~~

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//处理bean 自定义
    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)) {
                    this.parseDefaultElement(ele, delegate);
                } else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    } else {
//默认
        delegate.parseCustomElement(root);
    }
}

这里的两种方式是有区别的,一个是默认的,一个是需要自己定义的,我们都知道默认的不需要我们手动操作什么,如果是自定义的,那么就需要手动增加一些配置信息了,比如说我们要配置事物,就需要用到tx标签等,

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                     http://www.springframework.org/schema/beans/spring-beans.xsd
                     http://www.springframework.org/schema/tx
                     http://www.springframework.org/schema/tx/spring-tx.xsd
                     http://www.springframework.org/schema/aop
                     http://www.springframework.org/schema/aop/spring-aop.xsd "
>

加载的时候如果获取的空间命名为:http://www.springframework.org/schema/beans则为默认的
下面是对不同标签的处理方案

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, "import")) {
        this.importBeanDefinitionResource(ele);
    } else if (delegate.nodeNameEquals(ele, "alias")) {
        this.processAliasRegistration(ele);
    } else if (delegate.nodeNameEquals(ele, "bean")) {
        this.processBeanDefinition(ele, delegate);
    } else if (delegate.nodeNameEquals(ele, "beans")) {
        this.doRegisterBeanDefinitions(ele);
    }

}

我们先看一个的实现原理,其他的理解就比较容易了

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

        try {
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
        } catch (BeanDefinitionStoreException var5) {
            this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
        }

        this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//解析id属性
    String id = ele.getAttribute("id");
//解析name属性    
String nameAttr = ele.getAttribute("name");
    List<String> aliases = new ArrayList();
//分割name属性
    if (StringUtils.hasLength(nameAttr)) {
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
        aliases.addAll(Arrays.asList(nameArr));
    }
    String beanName = id;
    if (!StringUtils.hasText(id) && !aliases.isEmpty()) {
        beanName = (String)aliases.remove(0);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
        }
    }
    if (containingBean == null) {
        this.checkNameUniqueness(beanName, aliases, ele);
    }
    AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);
    if (beanDefinition != null) {
        if (!StringUtils.hasText(beanName)) {
            try {
//如果不存在 beanName那么根据spring中提供的命名规则为当前的bean生成对应的beanname
                if (containingBean != null) {
                    beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
                } else {
                    beanName = this.readerContext.generateBeanName(beanDefinition);
                    String beanClassName = beanDefinition.getBeanClassName();
                    if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                        aliases.add(beanClassName);
                    }
                }
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");
                }
            } catch (Exception var9) {
                this.error(var9.getMessage(), ele);
                return null;
            }
        }
        String[] aliasesArray = StringUtils.toStringArray(aliases);
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    } else {
        return null;
    }
}

这个方法开始没怎么看懂,但是有个点是清楚的,那就是解析xml属性,转换成bean的属性,bdHolder 这个对象中就有id,class,beanName,aliases等属性,在解析xml后,自动填充属性值,bdHolder不为null时,若存在默认标签的子节点下还存在自定义属性,还需要decorateBeanDefinitionIfRequired方法去解析,解析完成后,需要对bdholder进行注册,这个操作在registerBeanDefinition实现了,最后我们就是给spring发送通知,告诉他这个bean已经加载完成了,呼,是不是觉得一个很简单的东西,到这来看着就不是那么简单了。
思路大致是这样的:
1.DefaultBeanDefinitionDocumentReader 获取并装载xml文件
2.获取BeanDefinitionHolder 实例对象,
3.在对BeanDefinitionHolder 进行注册
4.通知spring,bean已经加载并注册完毕
上面有一个部分是画了红线的部分,我需要了解一下,我第一眼看的时候 以为我看错类了,因为从DefaultBeanDefinitionDocumentReader进到BeanDefinitionParserDelegate也是这个方法,代码如下:

public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
    this.parseState.push(new BeanEntry(beanName));
    String className = null;
//解析class属性
    if (ele.hasAttribute("class")) {
        className = ele.getAttribute("class").trim();
    }

    try {
        String parent = null;
//解析parent属性
        if (ele.hasAttribute("parent")) {
            parent = ele.getAttribute("parent");
        }
      //创建一个可以承载属性的 AbstractBeanDefinition
        AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
     //解析bean的各种属性(硬编码)
        this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//提取description
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
//解析元数据
        this.parseMetaElements(ele, bd);
//解析Lookup-method属性
        this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析Replaced-method属性
        this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析构造函数参数
        this.parseConstructorArgElements(ele, bd);
//解析property元素
        this.parsePropertyElements(ele, bd);
//解析qualificer元素
        this.parseQualifierElements(ele, bd);
        bd.setResource(this.readerContext.getResource());
        bd.setSource(this.extractSource(ele));
        AbstractBeanDefinition var7 = bd;
        return var7;
    } catch (ClassNotFoundException var13) {
        this.error("Bean class [" + className + "] not found", ele, var13);
    } catch (NoClassDefFoundError var14) {
        this.error("Class that bean class [" + className + "] depends on not found", ele, var14);
    } catch (Throwable var15) {
        this.error("Unexpected failure during bean definition parsing", ele, var15);
    } finally {
        this.parseState.pop();
    }
    return null;
}

这样bean标签的所有属性都解析都列出来了,but,好需要进一步解析
1.第一步需要一个实例来装载解析出来的属性,不然解析出来没地方放那就**了。
AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);

这里是创建AbstractBeanDefinition 实例的过程
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) {
            bd.setBeanClass(ClassUtils.forName(className, classLoader));
        } else {
            bd.setBeanClassName(className);
        }
    }
    return bd;
}

2.解析各种属性

this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) {
    if (ele.hasAttribute("singleton")) {
        this.error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
    } else if (ele.hasAttribute("scope")) {
        bd.setScope(ele.getAttribute("scope"));
    } else if (containingBean != null) {
        bd.setScope(containingBean.getScope());
    }
    if (ele.hasAttribute("abstract")) {
        bd.setAbstract("true".equals(ele.getAttribute("abstract")));
    }
    String lazyInit = ele.getAttribute("lazy-init");
    if ("default".equals(lazyInit)) {
        lazyInit = this.defaults.getLazyInit();
    }
    bd.setLazyInit("true".equals(lazyInit));
    String autowire = ele.getAttribute("autowire");
    bd.setAutowireMode(this.getAutowireMode(autowire));
    String dependencyCheck = ele.getAttribute("dependency-check");
    bd.setDependencyCheck(this.getDependencyCheck(dependencyCheck));
    String autowireCandidate;
    if (ele.hasAttribute("depends-on")) {
        autowireCandidate = ele.getAttribute("depends-on");
        bd.setDependsOn(StringUtils.tokenizeToStringArray(autowireCandidate, ",; "));
    }
    autowireCandidate = ele.getAttribute("autowire-candidate");
    String destroyMethodName;
    if (!"".equals(autowireCandidate) && !"default".equals(autowireCandidate)) {
        bd.setAutowireCandidate("true".equals(autowireCandidate));
    } else {
        destroyMethodName = this.defaults.getAutowireCandidates();
        if (destroyMethodName != null) {
            String[] patterns = StringUtils.commaDelimitedListToStringArray(destroyMethodName);
            bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
        }
    }
    if (ele.hasAttribute("primary")) {
        bd.setPrimary("true".equals(ele.getAttribute("primary")));
    }
    if (ele.hasAttribute("init-method")) {
        destroyMethodName = ele.getAttribute("init-method");
        if (!"".equals(destroyMethodName)) {
            bd.setInitMethodName(destroyMethodName);
        }
    } else if (this.defaults.getInitMethod() != null) {
        bd.setInitMethodName(this.defaults.getInitMethod());
        bd.setEnforceInitMethod(false);
    }
    if (ele.hasAttribute("destroy-method")) {
        destroyMethodName = ele.getAttribute("destroy-method");
        bd.setDestroyMethodName(destroyMethodName);
    } else if (this.defaults.getDestroyMethod() != null) {
        bd.setDestroyMethodName(this.defaults.getDestroyMethod());
        bd.setEnforceDestroyMethod(false);
    }
    if (ele.hasAttribute("factory-method")) {
        bd.setFactoryMethodName(ele.getAttribute("factory-method"));
    }
    if (ele.hasAttribute("factory-bean")) {
        bd.setFactoryBeanName(ele.getAttribute("factory-bean"));
    }
    return bd;
}

上面是spring 解析xml中常见的属性和一些不常用的属性 如果你们有兴趣的话 可以自己去看看对应的方法,下面几个方法都是用来解析其他元素,但是都是有正对性的提取和解析属性。

上面这部分是 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 这一段的内容,可能是有点多,有些也没讲的很全面,因为其他解析的原理都大致相同,

那下面我们在看看这个 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);,当前面不为空的时候 执行的是这个函数,这个函数的意思是:当Spring中的bean使用的是默认的标签配置时,但是其中的子元素使用了自定义的配置时,执行该函数,也就是这个函数是用来解析自定义类型配置的,

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
    return this.decorateBeanDefinitionIfRequired(ele, definitionHolder, (BeanDefinition)null);
}

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {
    BeanDefinitionHolder finalDefinition = definitionHolder;
    NamedNodeMap attributes = ele.getAttributes();

    for(int i = 0; i < attributes.getLength(); ++i) {
        Node node = attributes.item(i);
        finalDefinition = this.decorateIfRequired(node, finalDefinition, containingBd);
    }

    NodeList children = ele.getChildNodes();

    for(int i = 0; i < children.getLength(); ++i) {
        Node node = children.item(i);
        if (node.getNodeType() == 1) {
            finalDefinition = this.decorateIfRequired(node, finalDefinition, containingBd);
        }
    }

    return finalDefinition;
}

public BeanDefinitionHolder decorateIfRequired(Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {
    String namespaceUri = this.getNamespaceURI(node);
    if (!this.isDefaultNamespace(namespaceUri)) {
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler != null) {
            return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
        }

        if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) {
            this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
        } else if (this.logger.isDebugEnabled()) {
            this.logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
        }
    }

    return originalDef;
}

上面是针对自定义的属性解析,不过这里有点不同了,这里是首先获取属性或者元素的空间命名,以此来判断该元素户这话属性是否适用于自定义标签的解析条件,找出自定义类型所对应的namespaceHandler并进行进一步解析,

那么xml配置文件解析就解释到这来了,有兴趣的朋友可以去看看相关的文章。

前面做的都是为了后续的工作做准备,那么前面的内容已经准备好了,剩下的就是把解析好的数据注册到spring中了。

try {
    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
    this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
}


public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
//获取beanName 作为注册的唯一标识
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//注册所以的别名
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        String[] var4 = aliases;
        int var5 = aliases.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String alias = var4[var6];
            registry.registerAlias(beanName, alias);
        }
    }
}
上面源码中科院看出,注册有两种方式
1.beanName 注册 beandefinition  源码如下
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
   //注册前校验AbstractBeanDefinition的methodoverrides校验
校验methodoverride是否与工厂犯法并存或者不存在
            ((AbstractBeanDefinition)beanDefinition).validate();
        } catch (BeanDefinitionValidationException var9) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9);
        }
    }
    BeanDefinition oldBeanDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
//处理注册已经注册的beanname情况
    if (oldBeanDefinition != null) {
//如果对于的beanname已经注册而且在配置中配置了bean不允许覆盖,则抛出异常
        if (!this.isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound.");
        }

        if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
            }
        } else if (!beanDefinition.equals(oldBeanDefinition)) {
            if (this.logger.isInfoEnabled()) {
                this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
            }
        } else if (this.logger.isDebugEnabled()) {
            this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
        }
        this.beanDefinitionMap.put(beanName, beanDefinition);
    } else {
        if (this.hasBeanCreationStarted()) {
            Map var4 = this.beanDefinitionMap;
//在多线程中可能存在不同步的的问题 特此加同步锁
            synchronized(this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                if (this.manualSingletonNames.contains(beanName)) {
                    Set<String> updatedSingletons = new LinkedHashSet(this.manualSingletonNames);
                    updatedSingletons.remove(beanName);
                    this.manualSingletonNames = updatedSingletons;
                }
            }
        } else {
//记录beanname
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            this.manualSingletonNames.remove(beanName);
        }
//注册beandefinition
        this.frozenBeanDefinitionNames = null;
    }
    if (oldBeanDefinition != null || this.containsSingleton(beanName)) {
//重置所有beanname对于的缓存
        this.resetBeanDefinition(beanName);
    }
}

用beanName注册也就是把beanN作为注册的key,根据上面的源码来看 是这样的,不过除此之外,还做了其他的,
1).abstractbeandefinition 的属性校验
2).对beanname已经注册的情况处理,如果有设置不允许覆盖则抛异常,没有直接覆盖
3).加入了map缓存
4).清除解析钱留下的对应beanname的缓存

2.别名注册beandefinition

public void registerAlias(String name, String alias) {
    Assert.hasText(name, "'name' must not be empty");
    Assert.hasText(alias, "'alias' must not be empty");
    Map var3 = this.aliasMap;
    synchronized(this.aliasMap) {
//alias 和 beanname相同则不记录并删除对应的alias
        if (alias.equals(name)) {
            this.aliasMap.remove(alias);
        } else {
//如果alias不允许呗覆盖则抛出异常
            String registeredName = (String)this.aliasMap.get(alias);
            if (registeredName != null) {
                if (registeredName.equals(name)) {
                    return;
                }

                if (!this.allowAliasOverriding()) {
                    throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
                }
            }
                       //校验 当A->B 存在是,若再次出现A->C->B的时候回抛出异常,可以看源码
            this.checkForAliasCircle(name, alias);
            this.aliasMap.put(alias, name);        
        }
    }
}

在了解前面的注册原理之后,理解别名注册就简单多了,
1)alias与beanname相同情况时的处理,若alias与beanname并名称相同则不需要处理并删除原有的alias,
2)alias覆盖处理,若aliasName已经用并已经指向了另一个beanname则需要用户的设置进行处理
3)alias循环检查,当A->B存时,若再次出现A->C->B的时候回抛出异常
4)最后 注册 alias

那注册完了,就需要通知监听器,让spring知道,注册已经完成了。但是里面并没有做任何的逻辑处理,这里只是做了扩展,当开发的时候有特定的需要可以将其进行扩展,进行相应的逻辑处理。

this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 

public class EmptyReaderEventListener implements ReaderEventListener {
public void componentRegistered(ComponentDefinition componentDefinition) {
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值