代码入口

之前写文章都会啰啰嗦嗦一大堆再开始,进入【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> 存储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 public ClassPathXmlApplicationContext(String configLocation) throws BeansException {2     this(new String[] {configLocation}, true, null);3 }

1 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
2         throws BeansException {
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() throws BeansException, 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                 throw ex;
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 void prepareRefresh() {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 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
2     refreshBeanFactory();
3     ConfigurableListableBeanFactory beanFactory = getBeanFactory();
4     if (logger.isDebugEnabled()) {
5         logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
6     }
7     return beanFactory;
8 }

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

 1 protected final void refreshBeanFactory() throws BeansException {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<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  
================================================================================== 

转载于:https://www.cnblogs.com/moxiaowenxin/p/11169548.html

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

  1. Spring源码分析4---IOC加载过程补充总结

    原文出自:http://cmsblogs.com IOC 之 获取验证模型 DTD 与 XSD 的区别 DTD(Document Type Definition),即文档类型定义,为 XML 文件的验 ...

  2. spring源码之bean加载(bean解析下篇)

    bean的加载步骤: MyTestBean bean = (MyTestBean) bf.getBean("myTestBean"); 步骤: (1) 转换对应的beanName ...

  3. springboot集成mybatis源码分析-启动加载mybatis过程(二)

    springboot集成mybatis源码分析-启动加载mybatis过程(二) 1.springboot项目最核心的就是自动加载配置,该功能则依赖的是一个注解@SpringBootApplicati ...

  4. Mybatis3源码分析(05)-加载Configuration-加载MappedStatement

    2019独角兽企业重金招聘Python工程师标准>>> Mybatis3源码分析(05)-加载Configuration-加载MappedStatement 博客分类: java m ...

  5. beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览

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

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

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

  7. Spring源码分析——Bean的生命周期

    文章目录 说明 测试代码 说明 本文从源码的角度分析Spring中Bean的加载过程,本文使用的Spring版本为4.3.25.RELEASE 测试代码 测试代码如下,根据这段简单的测试代码,一步步跟 ...

  8. Glide 4.9源码解析-图片加载流程

    本文Glide源码基于4.9,版本下载地址如下:Glide 4.9 前言 由于Glide源码真的很复杂,因此本文只分析和贴出与图片加载流程相关的功能以及代码.另外本文Glide源码基于4.9,与3.x ...

  9. Launcher3源码分析(LauncherModel加载数据)

    LauncherModel继承BroadcastReceiver,显然是一个广播接收者.在上一篇Launcher的启动中讲到桌面数据的加载工作是在LauncherModel中执行的,那么它是如何加载数 ...

最新文章

  1. SQLite中的运算符表达式
  2. golang 得到字符串在文件中的行号
  3. 数据结构:基数排序(Radix sort)
  4. 个别学生计算机辅导计划,网络学院计算机基础统考辅导计划.doc
  5. SSH连接慢与反向解析(转)
  6. 力扣226-翻转二叉树(C++,附思路)
  7. C语言-数据结构-可变长顺序表的查找操作
  8. Android数据库升级、降级、创建(onCreate() onUpgrade() onDowngrade())的注意点
  9. 随机验证码(数字和字母组成)及toLowerCase() 字符串转小写方法和toUpperCase()字符串转大写方法...
  10. 在Java环境下怎么打开_Java环境配置及在Dos命令下运行Java程序
  11. shell脚本遍历文件夹下所有文件
  12. APICloud AVM框架列表组件list-view的使用、flex布局教程
  13. 软考-架构师知识总结
  14. 【建站笔记】:在wordpress博客文章中插入代码段并高亮显示
  15. 京东数据化运营(四)— 客单价篇
  16. python 估值模型_基于Python的客户价值细分模型(RFM)
  17. disallow root login remotely 不起作用?
  18. kaggle 泰坦尼克号数据分析 笔记
  19. MATLAB设计课题推荐及选择老师技巧
  20. 代码提交到GitHub时出现的反复报错

热门文章

  1. 2020 前端开源领域技术展望
  2. 什么是人机对话模型?阿里小蜜团队写了1.5万字
  3. 工程师如何给女友买包?问问阿里“百事通”
  4. mysql select union_MySQL SELECT语法(四)UNION语法详解
  5. yuzu模拟器linux,Yuzu Early Acces
  6. 一天学完spark的Scala基础语法教程五、闭包(idea版本)
  7. MySQL查询报错ERROR:No query specified
  8. 可变大小区(Variable-Size Extents)
  9. 历史命令与实时记录(redhat6.8)
  10. ROS-Solidworks转URDF