转载 https://mp.weixin.qq.com/s?__biz=MzI0NjUxNTY5Nw==&mid=2247483835&idx=1&sn=276911368d443f134997408a75578daa&scene=19#wechat_redirect

框架的源码分析,有些代码可以暂时忽略,如Spring如何进行XML模式校验的、XML解析的细节等,这些代码可以在了解了整体的原理之后,再做针对性的分析,关注重点内容即可,切记在一开始就去深挖每个细节,这样不仅会耗费很长时间,而且容易陷入某个坑里出不来。

以《深入理解Spring系列之一:开篇》示例中的ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationgContext.xml")为入口,进入源码内部,ClassPathXmlApplicationContext类图如下所示。

ClassPathXmlApplicationContext有多个构造方法,跟踪代码可以发现,最终使用的是下面这个方法,

	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

方法的参数很容易理解,configLocations指Spring的xml配置文件;refresh指是否需要刷新,这个refresh决定了是否进行bean解析、注册及实例化;parent指父ApplicationContext

setConfigLocations方法就是设置框架要加载的资源文件的位置。进入refresh方法,这个方法继承自AbstractApplicationContext,所以具体实现在AbstractApplicationContext类中,具体代码如下。

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//容器预先准备,记录容器启动时间和标记
prepareRefresh(); //创建bean工厂,里面实现了BeanDefinition的装载
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //配置bean工厂的上下文信息,如类装载器等
prepareBeanFactory(beanFactory); try {
//在BeanDefinition被装载后,提供一个修改BeanFactory的入口
postProcessBeanFactory(beanFactory); //在bean初始化之前,提供对BeanDefinition修改入口,PropertyPlaceholderConfigurer 在这里被调用
invokeBeanFactoryPostProcessors(beanFactory); //注册各种 BeanPostProcessors,用于在bean被初始化时进行拦截,进行额外初始化操作
registerBeanPostProcessors(beanFactory); //初始化MessageSource
initMessageSource(); //初始化上下文事件广播
initApplicationEventMulticaster(); //这是一个模板方法
onRefresh(); //注册监听器
registerListeners(); //初始化所有未初始化的非懒加载的单例Bean
finishBeanFactoryInitialization(beanFactory); //发布事件通知
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();
}
}
}

这个方法里面就是IOC容器初始化的大致步骤了。上面步骤的第二步完成了BeanDefinition的装载,进入obtainFreshBeanFactory方法,这个方法的具体实现也在AbstractApplicationContext类中,代码如下所示。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}

这里面主要关注refreshBeanFactory方法,这个方法在AbstractApplicationContext类中并未实现,具体实现在子类AbstractRefreshableApplicationContext中,代码如下。

@Override
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);
}
}

这个方法使用了final修饰,也就是不能被重写了。首先检查BeanFactory是否已经存在,如果存在则销毁并关闭,然后新建一个 BeanFactory,其实就是一个DefaultListableBeanFactory,这个DefaultListableBeanFactory就是《深入理解Spring系列之一:开篇》说的那个。然后进行BeanFactory的属性设置,设置是否允许重写BeanDefinition、是否允许循环引用,接着loadBeanDefinitions方法就是BeanDefinition载入的入口了,这个方法在AbstractRefreshableApplicationContext本类中并未实现,具体在其子类中实现,根据用途不同有多个实现子类,下一篇内容将分析基于最基本的解析xml方式的AbstractXmlApplicationContext类中的实现。

loadBeanDefinitions方法 的具体实现类

深入理解Spring系列之四:BeanDefinition装载前奏曲的更多相关文章

  1. 深入理解Spring系列之五:BeanDefinition装载

    转载 https://mp.weixin.qq.com/s/1_grvpJYe8mMIAnebMdz9Q 接上篇<深入理解Spring系列之四:BeanDefinition装载前奏曲>,进 ...

  2. 深入理解Spring系列之七:web应用自动装配Spring配置

    转载 https://mp.weixin.qq.com/s/Lf4akWFmcyn9ZVGUYNi0Lw 在<深入理解Spring系列之一:开篇>的示例代码中使用如下方式去加载Spring ...

  3. 深入理解Spring系列之六:bean初始化

    转载 https://mp.weixin.qq.com/s/SmtqoELzBEdZLo8wsSvUdQ <深入理解Spring系列之四:BeanDefinition装载前奏曲>中提到,对 ...

  4. 深入理解Spring系列之二:BeanDefinition解析

    转载 https://mp.weixin.qq.com/s?__biz=MzI0NjUxNTY5Nw==&mid=2247483814&idx=1&sn=ddf49931d55 ...

  5. 深入理解Spring系列之三:BeanFactory解析

    转载 https://mp.weixin.qq.com/s?__biz=MzI0NjUxNTY5Nw==&mid=2247483824&idx=1&sn=9b7c2603093 ...

  6. 深入理解Spring系列之一:开篇

    转载 https://mp.weixin.qq.com/s?__biz=MzI0NjUxNTY5Nw==&mid=2247483810&idx=1&sn=a2df14fdb63 ...

  7. 深入理解Spring系列之十二:@Transactional是如何工作的

    转载 https://mp.weixin.qq.com/s/ZwhkUQF1Nun9pNrFI-3a6w 首先从说起.配置了,就必定有对应的标签解析器类,查看NamespaceHandler接口的实现 ...

  8. 深入理解Spring系列之八:常用的扩展接口

    转载 https://mp.weixin.qq.com/s/XfhZltSlTall8wKwV_7fKg Spring不仅提供了一个进行快速开发的基础框架,而且还提供了很多可扩展的接口,用于满足一些额 ...

  9. 深入理解Spring系列之十:DispatcherServlet请求分发源码分析

    转载 https://mp.weixin.qq.com/s/-kEjAeQFBYIGb0zRpST4UQ DispatcherServlet是SpringMVC的核心分发器,它实现了请求分发,是处理请 ...

随机推荐

  1. laravel5.6 调用第三方类库

    大概流程: 1. 新建一个目录方类库 2. 配置composer配置文件 3. 在项目中使用终端运行composer  dumpautoload 4. 使用时 方法调用可以new对象后->方法名 ...

  2. C#和Java在多态情况下对成员访问的比较

    本文简单比较一下两种语言在里氏替换原则下,父类引用变量访问成员时的访问结果: 如果有两个类,如Person与Student,后者继承了前者,而且子类与父类有重名成员,当Person p = new S ...

  3. Java内存区域介绍

    Java虚拟机把内存划分成几个区域,每个区域都有各自的职责.下面将逐一分析每个区域. 有助于我们了解,每个方法,变量,对象等都去哪儿了! 程序计数器: 它占用一块很小的内存空间,可以看作是当前线程所执 ...

  4. jmeter链接多台负载机报错

    遇到常见的问题: 1.在Controller端上控制某台机器Run,提示“Bad call to remote host” 解决方案:检查被控制机器上的jmeter-server有没有启动,或者JMe ...

  5. 洛谷 P2647 最大收益

    我是题面 恩,贪心,鉴定完毕. 一个物品是否放进来,取决于它是否能对答案做出贡献. 那物品i的贡献就是\(w[i]-r[i]\) 可是收益的减少是会叠加的 那就是\(w[i]-j*r[i]\),j表示 ...

  6. Mysql局域网访问授权

    如果允许用户myuser从ip为192.168.1.1的主机连接到mysql服务器,并使用password作为密码 GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'1 ...

  7. 传说中的 SonarLint

    Sonar是一个用于代码质量管理的开源平台,用于管理源代码的质量 通过插件形式,可以支持包括java,C#,C/C++,PL/SQL,Cobol,JavaScrip,Groovy等等二十几种编程语言的 ...

  8. CodeChef DGCD

    You're given a tree on N vertices. Each vertex has a positive integer written on it, number on the i ...

  9. python部分知识归纳

  10. json&pickle序列化

    一.用途 我们需要将内存中的数据进行序列化,即写入文件中时,写入的类型只能是字符串或者二进制类型.但是如果我们想要将复杂一些的数据类型,如:列表.字典或者函数之类的同样进行序列化,我们就要用到 jso ...