Spring源码深度解析(XmlBeanFactory源码解析上)

    xiaoxiao2022-07-03  115

    Spring源码深度解析(XmlBeanFactory源码解析上)

    前言:

        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中,完成注册步骤

    最新回复(0)