文档章节

Spring core 源码分析 ---- IoC容器(一) 初始化

A
 AlexT
发布于 2017/03/21 19:40
字数 1823
阅读 147
收藏 0

IoC是什么,为什么要使用IoC容器?

Spring-framework-reference 的原文描述如下:

IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern. 

IoC也被称作依赖注入(DI)它是一个处理对象依赖项的过程,也就是将他们一起工作的其他的对象,只有通过构造参数、工厂方法参数或者(属性注入)通过构造参数实例化或通过工厂方法返回对象后再设置属性。当创建bean后,IoC容器再将这些依赖项注入进去。这个过程基本上是反转的,因此得名控制反转(IoC)。

IoC的作用:降低对象之间耦合,层与层之间的耦合。例如:接口的实现不用hard-coding 在上层业务中,而是通过IoC容器控制注入,这样上层业务完全可以不理会接口的实现,实现真正的面向接口编程。

容器的作用 : 将所有的beans(service,dao对象等)用容器托管,开发者无需关心这些beans的生命周期。

我们提到的IoC容器的底层实现最核心的接口是BeanFactory以及ApplicationContext。BeanFactory 提供了各种object的配置和管理,而ApplicationContext 则是beanFactory的子接口,集成了Spring AOP,message sources,BeanPostProcessor等一系列功能。

什么时候使用BeanFactory ,什么时候使用ApplicationContext?

在官方Spring Framework Reference Documentation(4.3.6.RELEASE) --- 7.16节中有非常详尽的描述如下:

BeanFactory提供了Spring IoC容器的基础支持,不过现在BeanFactory大多数使用在集成Spring的三方框架,以及Spring的历史用户中。 

而对于使用BeanFactory还是ApplicationContext,尽量使用ApplicationContext 除非你有非常好的理由不这么做(内存非常关键时,占用几个KB会有较大的影响)。

我们来看一张功能表格:

在Spring 中,BeanPostProcessor这个扩展点功能大量被使用,而BeanFactory是不支持这个的,那就意味着 Spring 事务,AOP 等一系列功能当你只是用BeanFactory都不可用。

所以说,在我们正常的企业级应用中,还是要使用ApplicationContext。

 

源码分析:

下面这段代码是最为常见的spring Demo,加载spring 配置文件得到ApplicationContext,通过ApplicationContext 的getBean 得到我们想要的bean实例。

public class Test {
    public static  void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        TestBean bean = context.getBean("test",TestBean.class);
        bean.printTest();
    }
}

ClassPathXmlApplicationContext的类图如下:

自下而上,从左到右,解释下每个接口和类的意义:

  • ClassPathXmlApplicationContext:是一个简单的一站式的基于Xml配置的ApplicationContext(GenericApplicationContext + XmlBeanDefinitionReader更加灵活)
  • AbstractXmlApplicationContext :实现了loadBeanDefinitions(XmlBeanDefinitionReader reader)
  • AbstractRefreshableConfigApplictionContext :实现了setConfigLocations,指定ConfigLocations,BeanNameAware接口用来设置bean的Name,InitializingBean接口中定义了方法afterPropertiesSet(),在BeanFactory设置完所有properties后被调用。
  • AbstractRefreshableApplicationContext:ApplicationContext的base class,实现了 multiple calls to “refresh()” ,每次调用都创建一个内部的bean factory 实例。
  • AbstractApplictionContext: 是ApplicationContext接口的抽象实现,实现了common context的功能,使用 Template Method 设计模式
  • DefaultResourceLoader:是ResourceLoader接口的实现,返回UrlResource or classPathResource
  • DisposableBean 接口:BeanFactory 销毁一个单例时调用。
  • ConfigurableApplicationContext:configuration和lifecycle相关方法封装在这里,避免被ApplicationContext的客户端代码调用。这里封装的方法应该只被用于ApplicationContext启动和停止的。
  • ApplicationContext: 核心接口,在程序运行时只读,若实现支持reloaded ,也可以被reloaded。(1)通过ListableBeanFactory接口一个ApplicationContext 提供 BeanFatory的方法访问Application的组件。(2)通过ResouceLoader接口 load file resources。 (3)通过ApplicationEventPublisher接口发布事件 给所有已注册的监听者。(4) 通过MessageSource接口 解析messages,支撑国际化。官方建议 定义一个梯度的contexts,一个父Context可以在整个web application中被使用,每一个sevlet都有一个独立的子context。
  • EnvironmentCapable :定义了getEnvironment()方法 得到Environment的引用,所有的applicaitonContext都是EnvironmentCapable的。
  • ListbleBeanFactory:BeanFactory接口的扩展接口,可以枚举所有bean实例。
  • BeanFactory:BeanFactory作为最原始同时也最重要的Ioc容器,它主要的功能是为依赖注入 (DI) 提供支持,也是访问bean容器的客户端视角。BeanFactory包含了一系列bean definitions,每一个Bean definitions都有一个字符串的唯一标识。BeanFactory通过Bean definition 返回singleton的(独立的-prototype,request,session)等不同scope的bean实例。
  • HierachicalBeanFactory:接口被bean factoris 实现,实现分层级的bean Factory。

Talk is cheap. Show you the code.

ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

AbstractApplicationContext.java:

	/**
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
                //将多段config的路径设置到configLocations里
		setConfigLocations(configLocations);
		if (refresh) {
                   //核心方法,刷新applicationContext
			refresh();
		}
	}

 

	@Override
	public void refresh() throws BeansException, IllegalStateException {
        //线程安全
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
            // 设置容器startup时间,和active标志位,初始化所有property source
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
            // 调用子类实现的refreshBeanFactory() 返回getBeanFactory
            // 关键方法,参见 AbstractRefreshableApplicationContext ---- refreshBeanFactory()
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
            // 配置beanFactory标准的容器特征,例如 类加载器 和 后置处理 
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
                // 设置BeanFactory的后置处理
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				// 调用BeanFactory的后置处理,
                invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
                // 注册Bean的后置处理器
				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.
                // 遍历所有BeanDefinitionNames,调用getBean(beanName),并且调用afterSingletonsInstantiated()
				finishBeanFactoryInitialization(beanFactory);

				// 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();
			}
		}
	}

AbstractRefreshableApplicationContext.java ----------------refreshBeanFactory()

/**
	 * This implementation performs an actual refresh of this context's underlying
	 * bean factory, shutting down the previous bean factory (if any) and
	 * initializing a fresh bean factory for the next phase of the context's lifecycle.
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
            //清空容器里所有bean,同时调用DisposableBean.destroy()
			destroyBeans();
            //beanFactory引用置为空
			closeBeanFactory();
		}
		try {
            //创建一个beanFactory 开始初始化
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
            //配置allowBeanDefinitionOverriding和allowCircularReferences属性
			customizeBeanFactory(beanFactory);
            //由子类实现,此处用的是AbstractXmlApplicationContext.java-----loadBeanDefinitions()
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

AbstractXmlApplicationContext -------- loadBeanDefinitions() 

	/**
	 * Loads the bean definitions via an XmlBeanDefinitionReader.
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
	 * @see #initBeanDefinitionReader
	 * @see #loadBeanDefinitions
	 */
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
        //此处将所有beanDefinitions 导入 beanFactory里,此处解析xml创建beanDefinition的code细节不表
		loadBeanDefinitions(beanDefinitionReader);
	}

至此,一个可用的ApplicationContext初始化完成。

© 著作权归作者所有

A
粉丝 1
博文 13
码字总数 28763
作品 0
海淀
私信 提问
深入理解Spring源码(一)-IOC容器的定位,载入,注册

前言:Spring源码继承,嵌套层次非常多,读起来非常容易晕,小伙伴们在看文章的时候一定要跟着文章的思路自己去源码里点一点,看一看,并且多看几次。就会越来越清晰。下面开始正题 1.Spring...

Meet相识_bfa5
2018/05/01
0
0
Spring MVC 原理探秘 - 容器的创建过程

1.简介 在上一篇文章中,我向大家介绍了 Spring MVC 是如何处理 HTTP 请求的。Spring MVC 可对外提供服务时,说明其已经处于了就绪状态。再次之前,Spring MVC 需要进行一系列的初始化操作。...

coolblog.xyz
2018/07/03
0
0
Spring源码解析系列之IOC容器(一)

前言 实际上我所有的博客都是原来对原来印象笔记里笔记内容的加工,关于Spring源码自己已经解析了很多遍,但是时间长总是忘记,写一篇博客权当加强记忆,也算再次学习下大师们的设计思想,思...

后厂村老司机
2018/06/02
0
0
Spring IOC 容器源码分析 - 创建原始 bean 对象

1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续。在上一篇文章中,我们从战略层面上领略了方法的全过程。本篇文章,我们就从战术的层面上,详细分析方法中的一个重要的调用,即...

coolblog.xyz
2018/06/06
0
0
Spring IOC 容器源码分析系列文章导读

1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本。经过十几年的迭代,现在的 Spring 框架已经非常成熟了。Spring 包含了众多模块,包括但不限于...

coolblog
2018/05/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

前端面试题汇总

一. HTML常见的兼容性 1.HTML5 标签在低版本浏览器不兼容 解决办法:使用html5shiv库,引入下列语句 <!--[if lte IE 8]> <script src="https://cdn.bootcss.com/html5shiv/r29/html5.js"></sc......

蓝小驴
29分钟前
7
0
OSChina 周四乱弹 —— 我气的脸都黑了!

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 小小编辑推荐《Red Battle》- 高橋李依 / 豊崎愛生 《Red Battle》- 高橋李依 / 豊崎愛生 手机党少年们想听歌,请使劲儿戳(这里) @丶Lion ...

小小编辑
42分钟前
554
22
找OSG教程, B站就有

https://www.bilibili.com/video/av64849038?from=search&seid=11632913960900279653

洛克人杰洛
今天
5
0
学习记录(day07-Vue组件、自定义属性、自定义事件)

[TOC] 1.1.1什么是组件 一个vue文件就是一个组件 组件将html标签/css样式/对应JS打包成一个整体,也可以理解钻进一个具有样式和特效的自定义标签。 一、编写组件(提供方)<template> <di...

庭前云落
今天
5
0
使用Prometheus监控SpringBoot应用

通过之前的文章我们使用Prometheus监控了应用服务器node_exporter,数据库mysqld_exporter,今天我们来监控一下你的应用。(本文以SpringBoot 2.1.9.RELEASE 作为监控目标) 编码 添加依赖 使...

JAVA日知录
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部