第三部分-Spring容器的基本实现(二)-spring容器初始化的refresh(一)

版权声明:转载请注明出处,谢谢合作! https://blog.csdn.net/u011709128/article/details/80875813

Spring容器的基本实现(二)-spring容器初始化的refesh(一)

上一篇,我们将到了构建标准环境,处理要加载配置文件的路径,如替换占位符等。

接下来我们讲容器初始化的refresh

回到看ClassPathXmlApplicationContext类,我们继续

public ClassPathXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();//spring容器初始化的refresh
        }
    }

通过refresh(),进入到AbstractApplicationContext这个类上

/** 用于“刷新”和“销毁”的同步监视器 */
private final Object startupShutdownMonitor = new Object();//先记住这玩意

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {//一开始就是用了同步锁,把同步监视器锁起来了。
            // Prepare this context for refreshing.
            prepareRefresh();//准备此上下文以进行刷新,序号1

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//告诉子类刷新内部bean工厂。序号2

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);//准备bean工厂以在此上下文中使用。序号3

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);//允许在上下文子类中对bean工厂进行后处理。序号4

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);//在上下文中调用注册为bean的工厂处理器。序号5

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);//注册拦截bean创建的bean处理器。序号6

                // Initialize message source for this context.
                initMessageSource();//初始化此上下文的消息源。序号7

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();//初始化此上下文的事件广播器。序号8

                // Initialize other special beans in specific context subclasses.
                onRefresh();//在特定上下文子类中初始化其他特殊bean。序号9

                // Check for listener beans and register them.
                registerListeners();//检查监听器bean并注册它们。序号10

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);//实例化所有其余(非延迟初始化,非懒加载)单例。序号11

                // Last step: publish corresponding event.
                finishRefresh();//最后一步:发布相应的事件。
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();//销毁已经创建的单例以避免资源动荡。

                // Reset 'active' flag.
                cancelRefresh(ex);//重置“有效”标志。

                // Propagate exception to caller.
                throw ex;//向调用者通知异常。
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();//重置Spring核心中的常见内核缓存,因为我们可能不再需要单例bean的元数据了...
            }
        }
    }

==================================我是分割线===========================================

这里的代码里的任务比较多,我们一个一个来看。

我们将任务清单一个个进行划分,命名序号,以便我们运行的时候会混乱。

序号1:prepareRefresh();//准备此上下文以进行刷新;
序号2:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//告诉子类刷新内部bean工厂。
序号3:prepareBeanFactory(beanFactory);//准备bean工厂以在此上下文中使用。

需要try起来的模块===================================
序号4:postProcessBeanFactory(beanFactory);//允许在上下文子类中对bean工厂进行后处理。
序号5:invokeBeanFactoryPostProcessors(beanFactory);//在上下文中调用注册为bean的工厂处理器。
序号6:registerBeanPostProcessors(beanFactory);//注册拦截bean创建的bean处理器。
序号7:initMessageSource();//初始化此上下文的消息源。
序号8:initApplicationEventMulticaster();//初始化此上下文的事件广播器。
序号9:onRefresh();//在特定上下文子类中初始化其他特殊bean。
序号10:registerListeners();//检查监听器bean并注册它们。
序号11:finishBeanFactoryInitialization(beanFactory);//实例化所有其余(非延迟初始化,非懒加载)单例。
序号12:finishRefresh();//最后一步:发布相应的事件。

catch 模块====================================
序号13:destroyBeans();//销毁已经创建的单例以避免资源动荡。
序号14:cancelRefresh(ex);//重置“有效”标志。

finally模块=======================================
序号15:resetCommonCaches();//重置Spring核心中的常见内核缓存,因为我们可能不再需要单例bean的元数据了…

扫描二维码关注公众号,回复: 3274485 查看本文章

首先是序号1:repareRefresh();//准备此上下文以进行刷新

/**
     * Prepare this context for refreshing, setting its startup date and
     * active flag as well as performing any initialization of property sources.
     */
    protected void prepareRefresh() {
        this.startupDate = System.currentTimeMillis();//获取系统当前时间戳
        this.closed.set(false);//表示这个上下文是否已经关闭的标志,false表示没关闭
        this.active.set(true);//表示此上下文当前是否处于活动状态的标志,true为活动

        if (logger.isInfoEnabled()) {
            logger.info("Refreshing " + this);
        }

        // Initialize any placeholder property sources in the context environment
        initPropertySources();//在上下文环境中初始化任何占位符属性源,这个好像没啥用,代码块是空的,有一句注释For subclasses: do nothing by default.默认情况下不做任何事情。

        // Validate that all properties marked as required are resolvable
        // see ConfigurablePropertyResolver#setRequiredProperties
        //这里做了两件事,
        // 1、getEnvironment获取环境,如果没有,创建一个,有则返回
        // 2、validateRequiredProperties定义一个缺少必要属性值异常,判断requiredProperties是否为空,为空就返回异常
        getEnvironment().validateRequiredProperties();


        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        //创建一个LinkedHashSet来装预发布的ApplicationEvents事件
        this.earlyApplicationEvents = new LinkedHashSet<>();//允许收集前期的ApplicationEvents事件,一旦广播器可用,即可发布...
    }

来看序号2:obtainFreshBeanFactory()
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()//告诉子类刷新内部bean工厂

/**
     * Tell the subclass to refresh the internal bean factory.
     * @return the fresh BeanFactory instance
     * @see #refreshBeanFactory()
     * @see #getBeanFactory()
     */
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();//这个实现对这个上下文的底层进行实际的刷新bean工厂,关闭以前的bean工厂(如果有的话)和初始化一个新鲜的bean工厂,用于上下文生命周期的下一个阶段。
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

这里跟踪refreshBeanFactory()

进入AbstractRefreshableApplicationContext类

    /**
     * 这个实现对这个上下文的底层进行实际的刷新bean工厂,关闭以前的bean工厂(如果有的话)和初始化一个新鲜的bean工厂,用于上下文生命周期的下一个阶段。
     */
    @Override
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {//实际上返回的是:this.beanFactory != null
            destroyBeans();//销毁beans
            closeBeanFactory();//关闭bean工厂
        }
        try {
            //DefaultListableBeanFactory为ioc容器之源头,如同伊甸园般的存在
            //为此上下文创建一个内部bean工厂。针对每个{@link #refresh()}尝试进行调用。
            //createBeanFactory()如果它实现了ConfigurableApplicationContext,则返回父上下文的内部bean工厂; 否则,返回父上下文本身。
            DefaultListableBeanFactory beanFactory = createBeanFactory();

            //为序列化目的指定一个id,如果需要,可以将此BeanFactory从此id反序列化回BeanFactory对象。
            beanFactory.setSerializationId(getId());

            //自定义此上下文使用的内部bean工厂。 针对每个{@link #refresh()}尝试进行调用。
            customizeBeanFactory(beanFactory);

            //通过XmlBeanDefinitionReader加载bean定义。
            loadBeanDefinitions(beanFactory);


            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

createBeanFactory()中的内部实现

    //为此上下文创建一个内部bean工厂。针对每个{@link #refresh()}尝试进行调用。
     protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }


    =====================getInternalParentBeanFactory()的相关代码====================

    //如果它实现了ConfigurableApplicationContext,则返回父上下文的内部bean工厂; 否则,返回父上下文本身。
    @Nullable
    protected BeanFactory getInternalParentBeanFactory() {
        return (getParent() instanceof ConfigurableApplicationContext) ?
                ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
    }

beanFactory.setSerializationId(getId())的内部实现

    /**
     * 为序列化目的指定一个id,如果需要,可以将此BeanFactory从此id反序列化回BeanFactory对象。
     */
    public void setSerializationId(@Nullable String serializationId) {
        if (serializationId != null) {
            //不为空执行在serializableFactories里加入一组值:键为serializationId,值为当前对象的弱引用
            serializableFactories.put(serializationId, new WeakReference<>(this));
        }
        else if (this.serializationId != null) {
            //从serializableFactories中移除键为当前对象属性serializationId的对象,也就是以前存入的当前对象的弱引用
            serializableFactories.remove(this.serializationId);
        }
        //设置属性serializationId为输入参数
        this.serializationId = serializationId;
    }

customizeBeanFactory(beanFactory)内部实现

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        if (this.allowBeanDefinitionOverriding != null) {
            //设置给beanFactory对象相应属性,此属性的含义:是否允许覆盖同名称的不同定义的对象
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.allowCircularReferences != null) {
            //设置给beanFactory对象相应属性,此属性的含义:是否允许bean之间循环引用
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

loadBeanDefinitions(beanFactory);内部实现

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 为给定的BeanFactory创建一个新的XmlBeanDefinitionReader。
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // 使用此上下文的资源加载环境配置bean定义阅读器。

        //将环境设置为AbstractApplicationContext中的ConfigurableEnvironment.environment
        //也就是初始创建的环境
        beanDefinitionReader.setEnvironment(this.getEnvironment());

        //设置ResourceLoader以用于资源位置。
        //如果指定ResourcePatternResolver,则bean定义读取器将能够将资源模式解析为资源数组。
        //默认是PathMatchingResourcePatternResolver,也可以通过ResourcePatternResolver接口解析资源模式。
        //将其设置为{null}表明绝对路径资源加载
        beanDefinitionReader.setResourceLoader(this);

        //设置要用于解析的SAX实体解析器。
        //创建解析器ResourceEntityResolver
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // 允许子类提供读取的自定义初始化,然后继续实际加载bean定义。
        initBeanDefinitionReader(beanDefinitionReader);

        //用给定的XmlBeanDefinitionReader加载bean定义。
        loadBeanDefinitions(beanDefinitionReader);
    }

//为给定的BeanFactory创建一个新的XmlBeanDefinitionReader。
new XmlBeanDefinitionReader(beanFactory)详解

//BeanFactory以BeanDefinitionRegistry的形式加载bean定义
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }
//super方法
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        this.registry = registry;

        // 确定要使用的ResourceLoader。
        if (this.registry instanceof ResourceLoader) {
            //ResourceLoader用于加载资源的策略接口(例如类路径或文件系统资源)
            //用于从类路径加载的伪URL前缀:“classpath:”
            this.resourceLoader = (ResourceLoader) this.registry;
        }
        else {
            //路径匹配资源模式解析器,可以理解为通配符自动加载符合路径规则的文件。
            //根据配置路径自动加载符合路径规则的xml文件、类文件等等
            //比如:"classpath:*"
            this.resourceLoader = new PathMatchingResourcePatternResolver();
        }

        // 尽可能继承环境
        // 如果registry的类型不属于EnvironmentCapable,那么将构建一个新的标准环境
        if (this.registry instanceof EnvironmentCapable) {
            this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
        }
        else {
            this.environment = new StandardEnvironment();
        }
    }

我们来看一下创建ResourceEntityResolver类的实现

    //为指定的ResourceLoader(通常是一个ApplicationContext)创建一个ResourceEntityResolver。
    //@param resourceLoader用于加载包含XML实体的ResourceLoader(或ApplicationContext)
public ResourceEntityResolver(ResourceLoader resourceLoader) {
        super(resourceLoader.getClassLoader());
        this.resourceLoader = resourceLoader;
    }

super

    /**
     * 创建一个DelegatingEntityResolver委派给默认的BeansDtdResolver和PluggableSchemaResolver
     */
    public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
        ////用于从Spring类路径(或JAR文件)加载DTD
        this.dtdResolver = new BeansDtdResolver();

        //使用默认映射文件模式“META-INF / spring.schemas”加载模式URL - >模式文件位置映射。
        //即XSD
        this.schemaResolver = new PluggableSchemaResolver(classLoader);
    }

这里需要讲一下关于xml的验证模式,关于xml文件的正确性,比较常用的验证模式有两种:DTD和XSD

DTD即文档类型定义,是一种xml约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。
可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。

//这里表示定义的DTD
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

我们的BeanFactoryTest.xml用得就是XSD
XML Schema语言就是XSD。XSD描述了XML的文档结构。
可以用一个指定的XSD来验证某个XML文档,以检查该XML文档是否符合其要求。
文档设计者可以通过XSD指定一个XML文档所允许的结构和内容,并可根据此检查一个XML文档是否有效。
XSD本身就是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它

//这里表示定义的XSD
<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">

在接下来就是initBeanDefinitionReader(beanDefinitionReader);

    //这里就干了一件事,设置reader的验证模式,初始化用于加载此上下文的bean定义的bean定义reader器。
    //this.validating设置是否使用XML验证。默认是true.
    protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
        reader.setValidating(this.validating);
    }

loadBeanDefinitions(beanDefinitionReader);内部实现

//用给定的XmlBeanDefinitionReader加载bean定义。
//bean工厂的生命周期由{@link #refreshBeanFactory}方法处理; 因此这个方法只是加载和/或注册bean定义。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();//获取配置资源,由于没有,所以是空
        if (configResources != null) {
            //空的跳过判断
            reader.loadBeanDefinitions(configResources);
        }
        //此处获取我们之前给定的bean.xml配置文件的位置地址。
        //断点里可以看到只有一个BeanFactoryTest.xml
        String[] configLocations = getConfigLocations();//获取配置文件位置

        if (configLocations != null) {
            //进入加载或注册bean
            reader.loadBeanDefinitions(configLocations);
        }
    }

深入reader.loadBeanDefinitions(configLocations);

    @Override
    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        Assert.notNull(locations, "Location array must not be null");
        int counter = 0;
        //此处for循环加载数组中的配置文件位置
        for (String location : locations) {
            counter += loadBeanDefinitions(location);
        }
        //返回条数
        return counter;
    }

继续深入loadBeanDefinitions(location);

    //此处直接return返回了。我们再继续深入挖坑
    @Override
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(location, null);
    }

深入挖坑loadBeanDefinitions(location, null);

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = getResourceLoader();//获取资源读取器

        //如果资源读取器为空,返回BeanDefinitionStoreException异常
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }

        //此处判断资源读取器的类型是通配符匹配器还是绝对路径加载器
        if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.资源模式匹配可用。
            try {
                //使用资源模式匹配读取资源,也就是读取bean信息
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                int loadCount = loadBeanDefinitions(resources);//
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
                return loadCount;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
            // Can only load single resources by absolute URL.只能通过绝对URL加载单个资源。
            Resource resource = resourceLoader.getResource(location);
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }

在断点经过getResourceLoader();的时候,我们看一下ResourceLoader的值

这里写图片描述
至此,我们的spring以及预热装载了许多东西了。
环境属性以及环境资源
设置XML验证模式
设置资源配置文件读取器
设置配置文件封装
等等。。。。。

我们继续往下看。。

getResources(location);根据路径进行判断,如何加载配置文件,如:classpath:或classpath:*以及绝对路径加载

    @Override
    public Resource[] getResources(String locationPattern) throws IOException {
        Assert.notNull(locationPattern, "Location pattern must not be null");

        //判断locationPattern是否以classpath*开头
        if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
            //一个类路径资源(可能有多个同名的资源)
            if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
                // 一个类路径资源模式
                return findPathMatchingResources(locationPattern);
            }
            else {
                // 具有给定名称的所有类路径资源
                return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
            }
        }
        else {
            // 通常只在此处的前缀后面查找模式,而在Tomcat上仅在“war:”协议的“* /”分隔符之后查找。
            int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
                    locationPattern.indexOf(':') + 1);
            if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
                // 一个文件模式
                return findPathMatchingResources(locationPattern);
            }
            else {
                // 具有给定名称的单个资源
                return new Resource[] {getResourceLoader().getResource(locationPattern)};
            }
        }
    }

由于我们是指定单个资源的位置,所以直接跳到最后一个读取
即:return new Resource[] {getResourceLoader().getResource(locationPattern)};

接下来继续分析

    @Override
    public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");

        //自定义协议解析,如果我们自定义了ProtocolResolver,是会优先调用的
        for (ProtocolResolver protocolResolver : this.protocolResolvers) {
            Resource resource = protocolResolver.resolve(location, this);
            //根据返回值是否为null判断自定义的ProtocolResolver是否解决了resource的加载
            if (resource != null) {
                return resource;
            }
        }

        //根据location的开头判断是哪一种Resource,eg: http,classpath,file等
        if (location.startsWith("/")) {//如果是'/'开头的
            return getResourceByPath(location);
        }
        else if (location.startsWith(CLASSPATH_URL_PREFIX)) {//如果是classpath开头的,进入此处
            return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
        }
        else {//如果都不是,则尝试解析,如果实在解析不了,则解析为资源路径。
            try {
                // 尝试将网址解析为网址...
                URL url = new URL(location);
                return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
            }
            catch (MalformedURLException ex) {
                // 没有URL - >解析为资源路径。
                return getResourceByPath(location);
            }
        }
    }

此处条件都不匹配,直接进入catch模块,解析为资源路径。
getResourceByPath(location);

    //返回给定路径中资源的资源句柄。默认实现支持类路径位置。 这应该适用于独立实现,但可以被覆盖,例如。 针对Servlet容器的实现。
    protected Resource getResourceByPath(String path) {
        return new ClassPathContextResource(path, getClassLoader());
    }

    /**
     * ClassPathResource通过实现ContextResource接口来显式表示一个与上下文相关的路径。
     */
    protected static class ClassPathContextResource extends ClassPathResource implements ContextResource {

        public ClassPathContextResource(String path, @Nullable ClassLoader classLoader) {
            super(path, classLoader);
        }

        @Override
        public String getPathWithinContext() {
            return getPath();
        }

        @Override
        public Resource createRelative(String relativePath) {
            String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
            return new ClassPathContextResource(pathToUse, getClassLoader());
        }
    }

至此,读取器以及加载bean的信息以及完成,我们返回AbstractBeanDefinitionReader

接续思路:代码运行至int loadCount = loadBeanDefinitions(resources);//从指定的资源位置加载bean定义。
仅仅加载bean的定义,该位置也可以是位置匹配模式,前提是此bean定义读取器的ResourceLoader是ResourcePatternResolver。

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = getResourceLoader();//获取已经设置过的读取器
        if (resourceLoader == null) {//判断读取器是否为空
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }

        //判断读取器的类型
        if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.
            //通配符读取器
            try {
                //将给定的位置模式解析为Resource对象,又跑了回去,进行判断解析方式。如classpath或是classpath:*
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);


                int loadCount = loadBeanDefinitions(resources);//通过抽象方法,进行封装资源文件
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
                return loadCount;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
            // Can only load single resources by absolute URL.
            //绝对路径读取器
            Resource resource = resourceLoader.getResource(location);
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }

loadBeanDefinitions(resources);//XML从指定的XML文件加载bean定义。
此处和上面不太一样,跑到XmlBeanDefinitionReader中,创建了一个对资源文件的编码进行处理的类

/**
     * Load bean definitions from the specified XML file.
     * @param resource the resource descriptor for the XML file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

继续往下loadBeanDefinitions(new EncodedResource(resource));

/**
     * Load bean definitions from the specified XML file.
     * @param encodedResource the resource descriptor for the XML file,
     * allowing to specify an encoding to use for parsing the file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {//判断日志是否启
            //记录日志
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        //内部创建了一个线程,创建ThreadLocal.ThreadLocalMap,是让同一个线程使用相同的currentResources 
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

        if (currentResources == null) {
            currentResources = new HashSet<>(4);
            //将currentResources放入ThreadLocal中
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }

        //将之前的封装好的resource添加到currentResources中,如果添加不进去,则抛出异常
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }

        //获取输入流信息
        try {
            //通过配置文件路径,创建输入流,encodedResource.resource.path=BeanFactoryTest.xml
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                //创建XML实体的输入源
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    //设置输入流的编码
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //真正工作在下面这句。。
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();//关闭输入流
            }
        }
        catch (IOException ex) {//抛出io操作异常
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);//currentResources移除掉本次加载的encodedResource
            if (currentResources.isEmpty()) {//如果currentResources为空
                //那么移除当前正在加载的资源
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

我们深入进去看看doLoadBeanDefinitions(inputSource, encodedResource.getResource());

    /**
     * 实际上从指定的XML文件加载bean定义。
     * Actually load bean definitions from the specified XML file.
     * @param inputSource the SAX InputSource to read from
     * @param resource the resource descriptor for the XML file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     * @see #doLoadDocument
     * @see #registerBeanDefinitions
     */
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            Document doc = doLoadDocument(inputSource, resource);//真正干活的方法,从指定的路径加载Document
            return registerBeanDefinitions(doc, resource);//注册Bean定义
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }

跟踪Document doc = doLoadDocument(inputSource, resource)

    /**
     * Actually load the specified document using the configured DocumentLoader.
     * @param inputSource the SAX InputSource to read from
     * @param resource the resource descriptor for the XML file
     * @return the DOM Document
     * @throws Exception when thrown from the DocumentLoader
     * @see #setDocumentLoader
     * @see DocumentLoader#loadDocument
     */
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        //inputSource,传入的XML实体的输入源
        //getEntityResolver(),返回要使用的EntityResolver,如果未指定,则构建默认解析器。再次判断ResourceLoader
        //this.errorHandler,异常处理器SimpleSaxErrorHandler
        //getValidationModeForResource(resource),获取指定的{@link Resource}的验证模式
        //isNamespaceAware(),是否要提供对XML名称空间的支持,目前是false
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }


    /**
     * 这里贴一下getValidationModeForResource
     * 获取指定的{@link Resource}的验证模式
     */
     protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = getValidationMode();
        if (validationModeToUse != VALIDATION_AUTO) {
            return validationModeToUse;
        }
        int detectedMode = detectValidationMode(resource);
        if (detectedMode != VALIDATION_AUTO) {
            return detectedMode;
        }
        // Hmm, we didn't get a clear indication... Let's assume XSD,
        // since apparently no DTD declaration has been found up until
        //嗯,我们没有得到明确的指示......让我们假设XSD,因为在检测停止之前(在找到文档的根标签之前)显然没有找到DTD声明。
        return VALIDATION_XSD;
    }

getEntityResolver()

    protected EntityResolver getEntityResolver() {
        if (this.entityResolver == null) {
            // Determine default EntityResolver to use.
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader != null) {
                //如果实体加载器不为空,使用指定的
                this.entityResolver = new ResourceEntityResolver(resourceLoader);
            }
            else {
                //构建默认的实体加载器
                this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
            }
        }
        return this.entityResolver;
    }

这里讲一下EntityResolver

对于解析一个XML,SAX首先读取该XML文档上的声明,根据声明去寻找相应的DTD定义,以便对文档进行一个验证。EntityResolver的作用是项目本身可以提供一个如何寻找DTD声明的方法,即由程序来实现寻找DTD声明的过程。

继续分析this.documentLoader.loadDocument()

    /**
     * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
     * XML parser.
     */
    @Override
    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

        //创建工厂,validationMode验证模式,是否要提供对XML名称空间的支持,目前是false
        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isDebugEnabled()) {
            logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
        }

        //
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource);
    }

创建工厂createDocumentBuilderFactory(validationMode, namespaceAware);

    protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
            throws ParserConfigurationException {

`       //创建新实例工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(namespaceAware);//是否要提供对XML名称空间的支持,目前是false

        //判断校验模式validationMode
        if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
            factory.setValidating(true);
            if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
                //设置XSD强制识别名称空间...
                factory.setNamespaceAware(true);
                try {
                    //设置工厂属性

                    //SCHEMA_LANGUAGE_ATTRIBUTE;用于配置模式语言进行验证的JAXP属性。
                    //String类型值为:http://java.sun.com/xml/jaxp/properties/schemaLanguage

                    //XSD_SCHEMA_LANGUAGE;指示XSD模式语言的JAXP属性值。
                    //String类型值为:http://www.w3.org/2001/XMLSchema
                    factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
                }
                catch (IllegalArgumentException ex) {
                    ParserConfigurationException pcex = new ParserConfigurationException(
                            "Unable to validate using XSD: Your JAXP provider [" + factory +
                            "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
                            "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
                    pcex.initCause(ex);
                    throw pcex;
                }
            }
        }

        return factory;//返回工厂
    }

createDocumentBuilder(factory, entityResolver, errorHandler);

    protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
            @Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
            throws ParserConfigurationException {
        //从XML文档获取DOM Document实例
        DocumentBuilder docBuilder = factory.newDocumentBuilder();
        if (entityResolver != null) {
            docBuilder.setEntityResolver(entityResolver);//给docBuilder设置实体解析器
        }
        if (errorHandler != null) {
            docBuilder.setErrorHandler(errorHandler);//设置异常处理
        }
        return docBuilder;//返回文档解析器对象
    }

builder.parse(inputSource);

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();//重置验证管理器
        }
        domParser.parse(is);//使用sun工具包,DOMParser真正执行解析xml文件,解析过程略。。
        Document doc = domParser.getDocument();//获得解析后的DOM
        domParser.dropDocumentReferences();//删除参考文档
        return doc;//返回解析后的Document
    }

返回doLoadDocument(inputSource, resource);//真正干活的方法,从指定的路径加载Document

此时已经结束了从指定路径加载Document,接下来一篇,我们讲Bean的注册

猜你喜欢

转载自blog.csdn.net/u011709128/article/details/80875813
今日推荐