代码入口

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

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

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

1 ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");2 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接口,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

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

AbstractApplicationContext

ClassPathXmlApplicationContext构造函数

看下ClassPathXmlApplicationContext的构造函数:

1 public ClassPathXmlApplicationContext(String configLocation) throwsBeansException {2 this(new String[] {configLocation}, true, null);3 }

1 public ClassPathXmlApplicationContext(String[] configLocations, booleanrefresh, ApplicationContext parent)2 throwsBeansException {3

4 super(parent);5 setConfigLocations(configLocations);6 if(refresh) {7 refresh();8 }9 }

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

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()方法的定义:

1 public void refresh() throwsBeansException, IllegalStateException {2 synchronized (this.startupShutdownMonitor) {3 //Prepare this context for refreshing.

4 prepareRefresh();5

6 //Tell the subclass to refresh the internal bean factory.

7 ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();8

9 //Prepare the bean factory for use in this context.

10 prepareBeanFactory(beanFactory);11

12 try{13 //Allows post-processing of the bean factory in context subclasses.

14 postProcessBeanFactory(beanFactory);15

16 //Invoke factory processors registered as beans in the context.

17 invokeBeanFactoryPostProcessors(beanFactory);18

19 //Register bean processors that intercept bean creation.

20 registerBeanPostProcessors(beanFactory);21

22 //Initialize message source for this context.

23 initMessageSource();24

25 //Initialize event multicaster for this context.

26 initApplicationEventMulticaster();27

28 //Initialize other special beans in specific context subclasses.

29 onRefresh();30

31 //Check for listener beans and register them.

32 registerListeners();33

34 //Instantiate all remaining (non-lazy-init) singletons.

35 finishBeanFactoryInitialization(beanFactory);36

37 //Last step: publish corresponding event.

38 finishRefresh();39 }40

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

43 destroyBeans();44

45 //Reset 'active' flag.

46 cancelRefresh(ex);47

48 //Propagate exception to caller.

49 throwex;50 }51 }52 }

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

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

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

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

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

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

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

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

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

prepareRefresh方法

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

1 /**

2 * Prepare this context for refreshing, setting its startup date and3 * active flag.4 */

5 protected voidprepareRefresh() {6 this.startupDate =System.currentTimeMillis();7 synchronized (this.activeMonitor) {8 this.active = true;9 }10

11 if(logger.isInfoEnabled()) {12 logger.info("Refreshing " + this);13 }14 }

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

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

2、将active标识位设置为true

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

obtainFreshBeanFactory方法

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

1 protectedConfigurableListableBeanFactory obtainFreshBeanFactory() {2 refreshBeanFactory();3 ConfigurableListableBeanFactory beanFactory =getBeanFactory();4 if(logger.isDebugEnabled()) {5 logger.debug("Bean factory for " + getDisplayName() + ": " +beanFactory);6 }7 returnbeanFactory;8 }

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

1 protected final void refreshBeanFactory() throwsBeansException {2 if(hasBeanFactory()) {3 destroyBeans();4 closeBeanFactory();5 }6 try{7 DefaultListableBeanFactory beanFactory =createBeanFactory();8 beanFactory.setSerializationId(getId());9 customizeBeanFactory(beanFactory);10 loadBeanDefinitions(beanFactory);11 synchronized (this.beanFactoryMonitor) {12 this.beanFactory =beanFactory;13 }14 }15 catch(IOException ex) {16 throw new ApplicationContextException("I/O error parsing bean definition source for " +getDisplayName(), ex);17 }18 }

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

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

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

对象名

类  型

作    用

归属类

aliasMap

Map

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

SimpleAliasRegistry

singletonObjects

Map

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

DefaultSingletonBeanRegistry

singletonFactories

Map

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

DefaultSingletonBeanRegistry

earlySingletonObjects

Map

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

DefaultSingletonBeanRegistry

registeredSingletons

Set

存储注册过的Bean名

DefaultSingletonBeanRegistry

singletonsCurrentlyInCreation

Set

存储当前正在创建的Bean名

DefaultSingletonBeanRegistry

disposableBeans

Map

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

DefaultSingletonBeanRegistry

factoryBeanObjectCache

Map

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

FactoryBeanRegistrySupport

propertyEditorRegistrars

Set

存储PropertyEditorRegistrar接口实现集合

AbstractBeanFactory

embeddedValueResolvers

List

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

AbstractBeanFactory

beanPostProcessors

List

存储 BeanPostProcessor接口实现列表

AbstractBeanFactory

mergedBeanDefinitions

Map

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

AbstractBeanFactory

alreadyCreated

Set

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

AbstractBeanFactory

ignoredDependencyInterfaces

Set

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

AbstractAutowireCapableBeanFactory

resolvableDependencies

Map

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

DefaultListableBeanFactory

beanDefinitionMap

Map

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

DefaultListableBeanFactory

beanDefinitionNames

List

存储Bean定义名称列表

DefaultListableBeanFactory

beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览相关推荐

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

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  2. Spring源码分析:Bean加载流程概览及配置文件读取

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

  3. 源码解析:Spring源码解析笔记(五)接口设计总览

    本文由colodoo(纸伞)整理 QQ 425343603 Java学习交流群(717726984) Spring解析笔记 启动过程部分已经完成,对启动过程源码有兴趣的朋友可以作为参考文章. 源码解析 ...

  4. Spring的bean加载流程

    IOC容器就像是一个工厂,里面有很多流水线生产出一个个产品(bean).bean的加载流程大概分为: 容器启动阶段 bean加载阶段 容器启动阶段: 1.配置元信息 当你生产物品的时候总得知道产品得规 ...

  5. spring源码解析之IOC容器(二)------加载和注册

    上一篇跟踪了IOC容器对配置文件的定位,现在我们继续跟踪代码,看看IOC容器是怎么加载和注册配置文件中的信息的.开始之前,首先我们先来了解一下IOC容器所使用的数据结构-------BeanDefin ...

  6. Android之 RecyclerView,CardView 详解和相对应的上拉刷新下拉加载

    为什么80%的码农都做不了架构师?>>>    随着 Google 推出了全新的设计语言 Material Design,还迎来了新的 Android 支持库 v7,其中就包含了 M ...

  7. Spring源码|解析深入Spring源码多图剖析@Configuration背后的BeanFactory后置处理器实现逻辑

    揭秘@Configuration的秘密之BeanFactory后置处理器 前序文章 Spring如何扫描工作目录下的Bean?|图文并茂讲解@Configuration的工作原理 文章目录 揭秘@Co ...

  8. JVM源码阅读-本地库加载流程和原理

    前言 本文主要研究OpenJDK中JVM源码中涉及到native本地库的加载流程和原理的部分.主要目的是为了了解本地库是如何被加载到虚拟机,以及是如何找到并执行本地库里的本地方法,以及JNI的 JNI ...

  9. vue 源码详解(零):Vue 源码流程图

    vue 源码详解(零):Vue 源码流程图 最近在研究 Vue 的源码, 整理博客, 结果想到的.看到的内容实在是太多了, 不知道从何写起, 故整理了一个大致的流程图,根据这个顺序进行一一整理. 为了 ...

最新文章

  1. linux oracle path恢复,Linux 环境下Oracle安装与调试(七)之SQL Loader,备份和恢复
  2. python基础知识资料-Python学习--最完整的基础知识大全
  3. 《树莓派用户指南(第3版)》——2.1 连接显示器
  4. Android ThreadUtil 线程公共类,判断是否在主线程/ 子线程执行 相关操作
  5. LeetCode 1796. 字符串中第二大的数字
  6. java quartz
  7. Apache Tomcat 再爆严重安全漏洞
  8. Daily Scrum M2 11-19
  9. [2018.03.29 T2] 公交旅行
  10. 调试ST电机库5.20遇到的问题
  11. 利用Jwing窗口写程序-----简单计算器(JAVA实用教程2-第五版 第九章 编程题 三(2)小题)
  12. web前端零基础html5 +css3基础教程
  13. 董宝珍:赔钱别赖公司 股民该怨自己
  14. 那些我接触过的「小而美」的公司
  15. VScode中crtl+鼠标左键无法跳转
  16. “食族人”商标不具有不良影响,二审被驳回上诉!
  17. Google Open Images Dataset V4 百度网盘地址。
  18. 苹果cmsv10仿9080YY电影网站红色大气响应式免费模板
  19. node.js把前台传来的base64码转成图片存放
  20. 目标检测之Loss:Center Loss梯度更新

热门文章

  1. DBASK数据库提问平台问题集萃,首批近二十位专家团曝光
  2. 前端实操案例丨如何实现JS向Vue传值
  3. 华为云PB级数据库GaussDB(for Redis)揭秘第十期:GaussDB(for Redis)迁移系列(上)
  4. 面试官:Java中线程是按什么顺序执行的?
  5. 普通人如何站在时代风口学好AI?这是我看过最好的答案
  6. #华为云·寻找黑马程序员# 如何实现一个优雅的Python的Json序列化库
  7. 【华为云动态】华为云开放日发布云专家激励计划,要将开发者“宠”上天
  8. MySQL指定存储引擎命令_MySQL常用指令(2)——存储引擎
  9. mysql 视图 速度慢_mysql 视图查询速度慢
  10. python基础知识学习笔记(2)