专栏首页java一日一条【Spring源码分析】Bean加载流程概览

【Spring源码分析】Bean加载流程概览

代码入口

之前写文章都会啰啰嗦嗦一大堆再开始,进入【Spring源码分析】这个板块就直接切入正题了。

很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已,Spring的加载过程相对是不太透明的,不太好去找加载的代码入口。

下面有很简单的一段代码可以作为Spring代码加载的入口:

ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
ac.getBean(XXX.class);

ClassPathXmlApplicationContext用于加载CLASSPATH下的Spring配置文件,可以看到,第二行就已经可以获取到Bean的实例了,那么必然第一行就已经完成了对所有Bean实例的加载,因此可以通过ClassPathXmlApplicationContext作为入口。为了后面便于代码阅读,先给出一下ClassPathXmlApplicationContext这个类的继承关系:

大致的继承关系是如上图所示的,由于版面的关系,没有继续画下去了,左下角的ApplicationContext应当还有一层继承关系,比较关键的一点是它是BeanFactory的子接口。

最后声明一下,本文使用的Spring版本为3.0.7,比较老,使用这个版本纯粹是因为公司使用而已。

ClassPathXmlApplicationContext存储内容

为了更理解ApplicationContext,拿一个实例ClassPathXmlApplicationContext举例,看一下里面存储的内容,加深对ApplicationContext的认识,以表格形式展现:

对象名

类 型

作 用

归属类

configResources

Resource[]

配置文件资源对象数组

ClassPathXmlApplicationContext

configLocations

String[]

配置文件字符串数组,存储配置文件路径

AbstractRefreshableConfigApplicationContext

beanFactory

DefaultListableBeanFactory

上下文使用的Bean工厂

AbstractRefreshableApplicationContext

beanFactoryMonitor

Object

Bean工厂使用的同步监视器

AbstractRefreshableApplicationContext

id

String

上下文使用的唯一Id,标识此ApplicationContext

AbstractApplicationContext

parent

ApplicationContext

父级ApplicationContext

AbstractApplicationContext

beanFactoryPostProcessors

List<BeanFactoryPostProcessor>

存储BeanFactoryPostProcessor接口,Spring提供的一个扩展点

AbstractApplicationContext

startupShutdownMonitor

Object

refresh方法和destory方法公用的一个监视器,避免两个方法同时执行

AbstractApplicationContext

shutdownHook

Thread

Spring提供的一个钩子,JVM停止执行时会运行Thread里面的方法

AbstractApplicationContext

resourcePatternResolver

ResourcePatternResolver

上下文使用的资源格式解析器

AbstractApplicationContext

lifecycleProcessor

LifecycleProcessor

用于管理Bean生命周期的生命周期处理器接口

AbstractApplicationContext

messageSource

MessageSource

用于实现国际化的一个接口

AbstractApplicationContext

applicationEventMulticaster

ApplicationEventMulticaster

Spring提供的事件管理机制中的事件多播器接口

AbstractApplicationContext

applicationListeners

Set<ApplicationListener>

Spring提供的事件管理机制中的应用监听器

AbstractApplicationContext

ClassPathXmlApplicationContext构造函数

看下ClassPathXmlApplicationContext的构造函数:

从第二段代码看,总共就做了三件事:

  1、super(parent)

    没什么太大的作用,设置一下父级ApplicationContext,这里是null

  2、setConfigLocations(configLocations)

    代码就不贴了,一看就知道,里面做了两件事情:

    (1)将指定的Spring配置文件的路径存储到本地

    (2)解析Spring配置文件路径中的${PlaceHolder}占位符,替换为系统变量中PlaceHolder对应的Value值,System本身就自带一些系统变量比如class.path、os.name、user.dir等,也可以通过System.setProperty()方法设置自己需要的系统变量

  3、refresh()

    这个就是整个Spring Bean加载的核心了,它是ClassPathXmlApplicationContext的父类AbstractApplicationContext的一个方法,顾名思义,用于刷新整个Spring上下文信息,定义了整个Spring上下文加载的流程。

refresh方法

上面已经说了,refresh()方法是整个Spring Bean加载的核心,因此看一下整个refresh()方法的定义:

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }
        }
    }

每个子方法的功能之后一点一点再分析,首先refresh()方法有几点是值得我们学习的:

  1、方法是加锁的,这么做的原因是避免多线程同时刷新Spring上下文

  2、尽管加锁可以看到是针对整个方法体的,但是没有在方法前加synchronized关键字,而使用了对象锁startUpShutdownMonitor,这样做有两个好处:

    (1)refresh()方法和close()方法都使用了startUpShutdownMonitor对象锁加锁,这就保证了在调用refresh()方法的时候无法调用close()方法,反之亦然,避免了冲突

    (2)另外一个好处不在这个方法中体现,但是提一下,使用对象锁可以减小了同步的范围,只对不能并发的代码块进行加锁,提高了整体代码运行的效率

  3、方法里面使用了每个子方法定义了整个refresh()方法的流程,使得整个方法流程清晰易懂。这点是非常值得学习的,一个方法里面几十行甚至上百行代码写在一起,在我看来会有三个显著的问题:

    (1)扩展性降低。反过来讲,假使把流程定义为方法,子类可以继承父类,可以根据需要重写方法

    (2)代码可读性差。很简单的道理,看代码的人是愿意看一段500行的代码,还是愿意看10段50行的代码?

    (3)代码可维护性差。这点和上面的类似但又有不同,可维护性差的意思是,一段几百行的代码,功能点不明确,不易后人修改,可能会导致“牵一发而动全身”

prepareRefresh方法

下面挨个看refresh方法中的子方法,首先是prepareRefresh方法,看一下源码:

/**
 * Prepare this context for refreshing, setting its startup date and
 * active flag.
 */
protected void prepareRefresh() {
    this.startupDate = System.currentTimeMillis();
        synchronized (this.activeMonitor) {
        this.active = true;
    }

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

这个方法功能比较简单,顾名思义,准备刷新Spring上下文,其功能注释上写了:

1、设置一下刷新Spring上下文的开始时间

2、将active标识位设置为true

另外可以注意一下12行这句日志,这句日志打印了真正加载Spring上下文的Java类。

obtainFreshBeanFactory方法

obtainFreshBeanFactory方法的作用是获取刷新Spring上下文的Bean工厂,其代码实现为:

/**
 * Prepare this context for refreshing, setting its startup date and
 * active flag.
 */
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}

其核心是第二行的refreshBeanFactory方法,这是一个抽象方法,有AbstractRefreshableApplicationContext和GenericApplicationContext这两个子类实现了这个方法,看一下上面ClassPathXmlApplicationContext的继承关系图即知,调用的应当是AbstractRefreshableApplicationContext中实现的refreshBeanFactory,其源码为:

/**
 * Prepare this context for refreshing, setting its startup date and
 * active flag.
 */
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

这段代码的核心是第7行,这行点出了DefaultListableBeanFactory这个类,这个类是构造Bean的核心类,这个类的功能会在下一篇文章中详细解读,首先给出DefaultListableBeanFactory的继承关系图:

AbstractAutowireCapableBeanFactory这个类的继承层次比较深,版面有限,就没有继续画下去了,本图基本上清楚地展示了DefaultListableBeanFactory的层次结构。

为了更清晰地说明DefaultListableBeanFactory的作用,列举一下DefaultListableBeanFactory中存储的一些重要对象及对象中的内容,DefaultListableBeanFactory基本就是操作这些对象,以表格形式说明:

对象名

类 型

作 用

归属类

aliasMap

Map<String, String>

存储Bean名称->Bean别名映射关系

SimpleAliasRegistry

singletonObjects

Map<String, Object>

存储单例Bean名称->单例Bean实现映射关系

DefaultSingletonBeanRegistry

singletonFactories

Map<String, ObjectFactory>

存储Bean名称->ObjectFactory实现映射关系

DefaultSingletonBeanRegistry

earlySingletonObjects

Map<String, Object>

存储Bean名称->预加载Bean实现映射关系

DefaultSingletonBeanRegistry

registeredSingletons

Set<String>

存储注册过的Bean名

DefaultSingletonBeanRegistry

singletonsCurrentlyInCreation

Set<String>

存储当前正在创建的Bean名

DefaultSingletonBeanRegistry

disposableBeans

Map<String, Object>

存储Bean名称->Disposable接口实现Bean实现映射关系

DefaultSingletonBeanRegistry

factoryBeanObjectCache

Map<String, Object>

存储Bean名称->FactoryBean接口Bean实现映射关系

FactoryBeanRegistrySupport

propertyEditorRegistrars

Set<PropertyEditorRegistrar>

存储PropertyEditorRegistrar接口实现集合

AbstractBeanFactory

embeddedValueResolvers

List<StringValueResolver>

存储StringValueResolver(字符串解析器)接口实现列表

AbstractBeanFactory

beanPostProcessors

List<BeanPostProcessor>

存储 BeanPostProcessor接口实现列表

AbstractBeanFactory

mergedBeanDefinitions

Map<String, RootBeanDefinition>

存储Bean名称->合并过的根Bean定义映射关系

AbstractBeanFactory

alreadyCreated

Set<String>

存储至少被创建过一次的Bean名集合

AbstractBeanFactory

ignoredDependencyInterfaces

Set<Class>

存储不自动装配的接口Class对象集合

AbstractAutowireCapableBeanFactory

resolvableDependencies

Map<Class, Object>

存储修正过的依赖映射关系

DefaultListableBeanFactory

beanDefinitionMap

Map<String, BeanDefinition>

存储Bean名称-->Bean定义映射关系

DefaultListableBeanFactory

beanDefinitionNames

List<String>

存储Bean定义名称列表

DefaultListableBeanFactory

本文分享自微信公众号 - java一日一条(mjx_java)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 面试中问的话题Spring工作原理

    Spring工作原理 内部最核心的就是IOC了, 动态注入,让一个对象的创建不用new了,可以自动的生产,这其实就是利用java里的反射 反射其实就是在...

    哲洛不闹
  • 关于Spring的69个面试问答——终极列表

    这篇文章总结了一些关于Spring框架的重要问题,这些问题都是你在面试或笔试过程中可能会被问到的。下次你再也不用担心你的面试了,Java Code Geeks这...

    哲洛不闹
  • 69 个经典 Spring 面试题和答案

    Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标...

    哲洛不闹
  • Spring 5的最终功能发行版即将来临

    2020 年 6 月 25 日Spring Framework团队发布博客宣布Spring Framework最新的里程碑版本Spring Framework ...

    码农小胖哥
  • 周末推荐阅读【第二弹】

    在校招中,问得最多的框架就是Spring,其余的Struts2、Hibernate、Mybatis等等框架很少会被问到。而Spring的问题问得最多的无非就是:...

    Java3y
  • Java面试总结

    前几天去了几家公司面试,果然,基本全部倒在二面上,无语啊。。。不过幸好,到最后拿到了环球易购的offer,打算就这么好好呆着了,学习学习,努力努力,下面讲讲这几...

    Zephery
  • 新的Spring OAuth2.0 授权服务器项目正式发布

    记不记得之前发过一篇文章 Spring 官方发起 Spring Authorization Server 项目。该项目是由Spring Security主导的一...

    码农小胖哥
  • 浅谈@Autowired注解shiyong

    因为Student 的两个bean id分别为student和student02,恰好我们的变量名也叫student和student02,故不会报错。假如变量名...

    编程大道
  • Spring 异步实现原理与实战分享

    最近因为全链路压测项目需要对用户自定义线程池 Bean 进行适配工作,我们知道全链路压测的核心思想是对流量压测进行标记,因此我们需要给压测的流量请求进行打标,并...

    张乘辉
  • 生产系统中只读表的实现思路(r2第43天)

    在生产系统中有些时候需要保证一些表的只读特性,不允许表的数据被轻易修改。可能有一下的场景比较适用。 1) 一些系统中有一些类似数据字典信息的表。这些表的信息基本...

    jeanron100

扫码关注云+社区

领取腾讯云代金券