Spring源码解析系列文章(三)——DefaultListableBeanFactory、XmlBeanFactory

目录

1.在Spring中的作用

2.类图

3.类图分析

4、XmlBeanFactory

XmlBeanFactory示例代码

4.1 示例代码讲解:XmlBeanDefinitionReader

4.1.1 配置文件封装 ClassPathResource

4.1.2 加载 Bean (XmlBeanDefinitionReader.loadBeanDefinitions(ClassPathResource))

5、getBean


1.在Spring中的作用

DefaultListableBeanFactory 是整个bean加载的核心部分,是 Spring 注册及加载bean的默认实现。

2.类图

如上图,红框内为 DefaultListableBeanFactory 的方法分类,按照继承的类进行分类,例如下图,分别继承自对应类的方法

3.类图分析

DefaultListableBeanFactory 继承了这些接口的特性,实现了对bean的注册后处理。

 

4、XmlBeanFactory

XmlBeanFactory 对DefaultListableBeanFactory进行了扩展,主要用于从 XML 配置文件中读取 BeanDefinition,对于注册和获取 Bean 都是使用父类 DefaultListableBeanFactory 继承的方法去实现。

XMLBeanFactory 中增加了 XmlBeanDefinitionReader 类型的reader 对资源文件进行读取和注册。

Resource 定位配置文件 --》 XmlBeanDefinitionReader 解析 Resource 读取注册BeanDefinition

XmlBeanFactory示例代码

application-context.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.suning.spring.boot.train.domain.User">
        <property name="name" value="zhangsan"/>
        <property name="id" value="1"/>
    </bean>
</beans>

User

public class User {

    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

XmlBeanFactory 加载配置文件,初始化bean,并获取容器内bean的过程代码:

DefaultListableBeanFactory defaultListableBeanFactory = new XmlBeanFactory(new ClassPathResource("application-context.xml"));
User user = defaultListableBeanFactory.getBean(User.class);
System.out.println(user);

运行结果

 

4.1 示例代码讲解:XmlBeanDefinitionReader

Reader是解析 Resource配置文件的,需要先了解 applicatio-context.xml 文件怎么变成 Resource的

4.1.1 配置文件封装 ClassPathResource

Spring中用 Resource 接口封装底层资源,统一管理,例如application-context.xml。

扩展知识:ResourceLoader

Resource实现有:文件(FileSystemResource)、ClassPath资源(ClassPathResource)、URL资源(URLResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等。

ClassPathResource 源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.core.io;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

public class ClassPathResource extends AbstractFileResolvingResource {
    // 文件路径,例如 application-context.xml文件
    private final String path;
    // 类加载器
    @Nullable
    private ClassLoader classLoader;
    @Nullable
    private Class<?> clazz;

    public ClassPathResource(String path) {
        this(path, (ClassLoader)null);
    }

    // 构造函数
    public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
        Assert.notNull(path, "Path must not be null");
        String pathToUse = StringUtils.cleanPath(path);
        if(pathToUse.startsWith("/")) {
            pathToUse = pathToUse.substring(1);
        }
        // 初始化文件路径
        this.path = pathToUse;
        // 指定类加载器,如果没有传类加载器,则用默认的类加载器,翻看源码是:Thread.currentThread().getContextClassLoader()
        this.classLoader = classLoader != null?classLoader:ClassUtils.getDefaultClassLoader();
    }

    public ClassPathResource(String path, @Nullable Class<?> clazz) {
        Assert.notNull(path, "Path must not be null");
        this.path = StringUtils.cleanPath(path);
        this.clazz = clazz;
    }

    /** @deprecated */
    @Deprecated
    protected ClassPathResource(String path, @Nullable ClassLoader classLoader, @Nullable Class<?> clazz) {
        this.path = StringUtils.cleanPath(path);
        this.classLoader = classLoader;
        this.clazz = clazz;
    }

    // 获取配置文件路径
    public final String getPath() {
        return this.path;
    }
    // 获取类加载器
    @Nullable
    public final ClassLoader getClassLoader() {
        return this.clazz != null?this.clazz.getClassLoader():this.classLoader;
    }
    // 判断文件是否存在
    public boolean exists() {
        return this.resolveURL() != null;
    }

    @Nullable
    protected URL resolveURL() {
        return this.clazz != null?this.clazz.getResource(this.path):(this.classLoader != null?this.classLoader.getResource(this.path):ClassLoader.getSystemResource(this.path));
    }
    // 此方法继承自InputStreamSource,ClassPathResource实现了具体逻辑
    // 主要就是通过类加载器加载配置文件到内存 InputStream中
    public InputStream getInputStream() throws IOException {
        InputStream is;
        if(this.clazz != null) {
            is = this.clazz.getResourceAsStream(this.path);
        } else if(this.classLoader != null) {
            is = this.classLoader.getResourceAsStream(this.path);
        } else {
            is = ClassLoader.getSystemResourceAsStream(this.path);
        }

        if(is == null) {
            throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
        } else {
            return is;
        }
    }

    public URL getURL() throws IOException {
        URL url = this.resolveURL();
        if(url == null) {
            throw new FileNotFoundException(this.getDescription() + " cannot be resolved to URL because it does not exist");
        } else {
            return url;
        }
    }

    public Resource createRelative(String relativePath) {
        String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
        return this.clazz != null?new ClassPathResource(pathToUse, this.clazz):new ClassPathResource(pathToUse, this.classLoader);
    }

    @Nullable
    public String getFilename() {
        return StringUtils.getFilename(this.path);
    }

    public String getDescription() {
        StringBuilder builder = new StringBuilder("class path resource [");
        String pathToUse = this.path;
        if(this.clazz != null && !pathToUse.startsWith("/")) {
            builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
            builder.append('/');
        }

        if(pathToUse.startsWith("/")) {
            pathToUse = pathToUse.substring(1);
        }

        builder.append(pathToUse);
        builder.append(']');
        return builder.toString();
    }

    public boolean equals(@Nullable Object other) {
        if(this == other) {
            return true;
        } else if(!(other instanceof ClassPathResource)) {
            return false;
        } else {
            ClassPathResource otherRes = (ClassPathResource)other;
            return this.path.equals(otherRes.path) && ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz);
        }
    }

    public int hashCode() {
        return this.path.hashCode();
    }
}

如以上代码中注释的,ClassPathResource 初始化时主要保存 classpath路径下配置文件路径,选择类加载器,然后在继承自 InputStreamResource 的方法 getInputStream中 通过 类加载器加载配置文件到内存(InputStream)中,供Reader使用。

扩展:InputStreamResource 的 getInputStream方法在其他 Resource中的实现

Spring配置文件封装成 Resource 后,读取工作就交给 XmlBeanDefinitionReader来处理。

XmlBeanFactory 源码

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);
        // 解析 Resource 并加载 BeanDefinition
        this.reader.loadBeanDefinitions(resource);
    }
}

如上所示:this.reader.loadBeanDefinitions(resource) 才是加载bean 的方法

4.1.2 加载 Bean (XmlBeanDefinitionReader.loadBeanDefinitions(ClassPathResource))

 


public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    public static final int VALIDATION_NONE = 0;
    public static final int VALIDATION_AUTO = 1;
    public static final int VALIDATION_DTD = 2;
    public static final int VALIDATION_XSD = 3;
    private static final Constants constants = new Constants(XmlBeanDefinitionReader.class);
    private int validationMode = 1;
    private boolean namespaceAware = false;
    private Class<? extends BeanDefinitionDocumentReader> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
    private ProblemReporter problemReporter = new FailFastProblemReporter();
    private ReaderEventListener eventListener = new EmptyReaderEventListener();
    private SourceExtractor sourceExtractor = new NullSourceExtractor();
    @Nullable
    private NamespaceHandlerResolver namespaceHandlerResolver;
    private DocumentLoader documentLoader = new DefaultDocumentLoader();
    @Nullable
    private EntityResolver entityResolver;
    private ErrorHandler errorHandler;
    private final XmlValidationModeDetector validationModeDetector;
    private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded;

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
        this.errorHandler = new SimpleSaxErrorHandler(this.logger);
        this.validationModeDetector = new XmlValidationModeDetector();
        this.resourcesCurrentlyBeingLoaded = new NamedThreadLocal("XML bean definition resources currently being loaded");
    }

    public void setValidating(boolean validating) {
        this.validationMode = validating?1:0;
        this.namespaceAware = !validating;
    }

    public void setValidationModeName(String validationModeName) {
        this.setValidationMode(constants.asNumber(validationModeName).intValue());
    }

    public void setValidationMode(int validationMode) {
        this.validationMode = validationMode;
    }

    public int getValidationMode() {
        return this.validationMode;
    }

    public void setNamespaceAware(boolean namespaceAware) {
        this.namespaceAware = namespaceAware;
    }

    public boolean isNamespaceAware() {
        return this.namespaceAware;
    }

    public void setProblemReporter(@Nullable ProblemReporter problemReporter) {
        this.problemReporter = (ProblemReporter)(problemReporter != null?problemReporter:new FailFastProblemReporter());
    }

    public void setEventListener(@Nullable ReaderEventListener eventListener) {
        this.eventListener = (ReaderEventListener)(eventListener != null?eventListener:new EmptyReaderEventListener());
    }

    public void setSourceExtractor(@Nullable SourceExtractor sourceExtractor) {
        this.sourceExtractor = (SourceExtractor)(sourceExtractor != null?sourceExtractor:new NullSourceExtractor());
    }

    public void setNamespaceHandlerResolver(@Nullable NamespaceHandlerResolver namespaceHandlerResolver) {
        this.namespaceHandlerResolver = namespaceHandlerResolver;
    }

    public void setDocumentLoader(@Nullable DocumentLoader documentLoader) {
        this.documentLoader = (DocumentLoader)(documentLoader != null?documentLoader:new DefaultDocumentLoader());
    }

    public void setEntityResolver(@Nullable EntityResolver entityResolver) {
        this.entityResolver = entityResolver;
    }

    protected EntityResolver getEntityResolver() {
        if(this.entityResolver == null) {
            // 加载Resource的方法类,https://blog.csdn.net/ruanhao1203/article/details/103493017
            ResourceLoader resourceLoader = this.getResourceLoader();
            if(resourceLoader != null) {
                // SAX解析xml用到的EntityResolver:https://blog.csdn.net/ruanhao1203/article/details/103489931
                this.entityResolver = new ResourceEntityResolver(resourceLoader);
            } else {
                this.entityResolver = new DelegatingEntityResolver(this.getBeanClassLoader());
            }
        }

        return this.entityResolver;
    }

    public void setErrorHandler(ErrorHandler errorHandler) {
        this.errorHandler = errorHandler;
    }

    public void setDocumentReaderClass(Class<? extends BeanDefinitionDocumentReader> documentReaderClass) {
        this.documentReaderClass = documentReaderClass;
    }

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

    /**
     * 1. ClassPathResource 被包装成 EncodedResource
     * 2. EncodedResource中取出 InputStream 后包装成 org.xml.sax.InputSource(准备通过SAX方式读取XML文件)
     * 3. 继续调用 doLoadBeanDefinitions
     */
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if(this.logger.isTraceEnabled()) {
            this.logger.trace("Loading XML bean definitions from " + encodedResource);
        }
        // 通过属性记录已经加载的资源
        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 int loadBeanDefinitions(InputSource inputSource) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(inputSource, "resource loaded through SAX InputSource");
    }

    public int loadBeanDefinitions(InputSource inputSource, @Nullable String resourceDescription) throws BeanDefinitionStoreException {
        return this.doLoadBeanDefinitions(inputSource, new DescriptiveResource(resourceDescription));
    }

    /**
    * 1.获取xml文件的验证模式
    * 2.加载xml文件解析后获取 Document
    * 3.根据返回的Document 注册Bean信息
    */
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
            Document doc = this.doLoadDocument(inputSource, resource);
            int count = this.registerBeanDefinitions(doc, resource);
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("Loaded " + count + " bean definitions from " + resource);
            }

            return count;
        } catch (BeanDefinitionStoreException var5) {
            throw var5;
        } catch (SAXParseException var6) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
        } catch (SAXException var7) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
        } catch (ParserConfigurationException var8) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
        } catch (IOException var9) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
        } catch (Throwable var10) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
        }
    }
    // 获取xml解析后的 Document:https://blog.csdn.net/ruanhao1203/article/details/103487888
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        /**
         * inputSource: SAX包装EncodedResource 后的
         * this.getEntityResolver():SAX用,https://blog.csdn.net/ruanhao1203/article/details/103489931
         * this.errorHandler:SAX用
         * this.getValidationModeForResource(resource):验证 spring 的 是需要 DTD 模式还是 XSD模式
         * this.isNamespaceAware()
         */
        return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
    }
    /**
    * 如果 XMLBeanDefinitionReader.setValidationMode 设置了验证模式,则用设置的,
    * 否则使用自动检测模式,this.detectValidationMode 委托给 XmlValidationModeDetector 验证 
    * spring 的 是需要 DTD 模式还是 XSD模式
    */
    protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = this.getValidationMode();
        if(validationModeToUse != 1) {
            return validationModeToUse;
        } else {
            int detectedMode = this.detectValidationMode(resource);
            return detectedMode != 1?detectedMode:3;
        }
    }

    // 委托给 XmlValidationModeDetector 验证 spring 的 是需要 DTD 模式还是 XSD模式
    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 {
                // 委托给 XmlValidationModeDetector,验证 spring 的 是需要 DTD 模式还是 XSD模式,
                // 对应源码:https://blog.csdn.net/ruanhao1203/article/details/103488757
                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);
            }
        }
    }

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        int countBefore = this.getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }

    protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return (BeanDefinitionDocumentReader)BeanUtils.instantiateClass(this.documentReaderClass);
    }

    public XmlReaderContext createReaderContext(Resource resource) {
        return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, this.getNamespaceHandlerResolver());
    }

    public NamespaceHandlerResolver getNamespaceHandlerResolver() {
        if(this.namespaceHandlerResolver == null) {
            this.namespaceHandlerResolver = this.createDefaultNamespaceHandlerResolver();
        }

        return this.namespaceHandlerResolver;
    }

    protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
        ClassLoader cl = this.getResourceLoader() != null?this.getResourceLoader().getClassLoader():this.getBeanClassLoader();
        return new DefaultNamespaceHandlerResolver(cl);
    }
}

如上代码:

1.ClassPathResource 包装成 EncodedResource ,Encoded 编码,见名知意,就是用过指定编码读取配置文件的。

2.EncodedResource 继续包装成 InputResource

3.继续调用 doLoadBeanDefinitions

// 获取 XML 验证模式是 DTD还是XSD
// 解析xml 获取 Document
Document doc = this.doLoadDocument(inputSource, resource);
// 根据 Document 注册bean
int count = this.registerBeanDefinitions(doc, resource);

 这里分为两部分

解析 xml为Document

注册bean

至此bean 放入spring容器就结束了

5、getBean

 

 

 

 

 

 

 

 

 

 

发布了178 篇原创文章 · 获赞 47 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/ruanhao1203/article/details/103443209
今日推荐