spring IOC简单分析
Spring IOC 体系结构
- BeanFactory(BeanFactory 里只对 IOC 容器的基本行为作了定义,根本不关心你的 bean 是如何定义怎样加载的。正如我们只关心工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心)
Spring Bean 的创建是典型的工厂模式,这一系列的 Bean 工厂,也即 IOC 容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在 Spring 中有许多的 IOC 容器的实现供用户选择和使用
其中 BeanFactory 作为最顶层的一个接口类,它定义了 IOC 容器的基本功能规范,BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。
默认实现类是 DefaultListableBeanFactory,他实现了所有的接口。那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有他使用的场合,
它主要是为了区分在Spring内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。
例如  ListableBeanFactory  接口表示这些Bean是可列表的,而HierarchicalBeanFactory表示的是这些	Bean是有继承关系的,也就是每个Bean有可能有父Bean。	
AutowireCapableBeanFactory  接口定义Bean的自动装配规则。这四个接口共同定义了Bean的集合、Bean之间的关系、以及Bean行为.
最基本的 IOC容器接口 BeanFactory
- BeanDefinition
Bean 对象在 Spring 实现中是以BeanDefinition 来描述的
Bean 的解析主要就是对 Spring 配置文件的解析
- IOC 容器的初始化
IOC 容器的初始化包括 BeanDefinition 的 Resource 定位、载入和注册这三个基本的过程
- ioc 容器的创建过程 
- XmlBeanFactory:调用全过程:定位、载入、注册 
//根据 Xml 配置文件创建 Resource 资源对象,该对象中包含了 BeanDefinition 的信息
ClassPathResource resource =new ClassPathResource("application-context.xml");
public class XmlBeanFactory extends DefaultListableBeanFactory {
    //XmlBeanDefinitionReader 读取器,用于载入 BeanDefinition。之所以需要 BeanFactory 作为参数,是因为会将读取的信息回调配置给 factory
    //this 就是factory对象
    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
    /**
     * Create a new XmlBeanFactory with the given resource,
     * which must be parsable using DOM.
     * @param resource the XML resource to load bean definitions from
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }
    /**
     * Create a new XmlBeanFactory with the given input stream,
     * which must be parsable using DOM.
     * @param resource the XML resource to load bean definitions from
     * @param parentBeanFactory parent bean factory
     * @throws BeansException in case of loading or parsing errors
     * XmlBeanDefinitionReader 执行载入 BeanDefinition 的方法,最后会完成 Bean 的载入和注册。完成后 Bean 就成功的放置到 IOC 容器当中,以后我们就可以从中取得 Bean 来使用
     */
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader.loadBeanDefinitions(resource);
    }
}
- FileSystemXmlApplicationContext 的 IOC 容器流程 - public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException { 
 //所有构造方法都调用此构造
 this(configLocations, refresh, null);
 }- // 所有构造方法都调用此构造 
 public FileSystemXmlApplicationContext(
 String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
 throws BeansException {- //调用父类容器的构造方法(super(parent)方法)为容器设置好 Bean 资源加载器
 //父类。。super最终找到
 //动态的确定用哪个加载器去加载我们的配置文件
 /**
 public AbstractApplicationContext(@Nullable ApplicationContext parent) {
 //调用构造方法
 /**
 public AbstractApplicationContext() {
 //getResourcePatternResolver()
 /**
 //获取一个 Spring Source 的加载器用于读入 Spring Bean 定义资源文件
 protected ResourcePatternResolver getResourcePatternResolver() {
 //AbstractApplicationContext 继承 DefaultResourceLoader,因此也是一个资源加载器
 // Spring 资源加载器,其 getResource(String location)方法用于载入资源
 return new PathMatchingResourcePatternResolver(this);
 }
 */
 this.resourcePatternResolver = getResourcePatternResolver();
 }
 */
 this();
 setParent(parent);
 }
 */
 super(parent);
 //调用父类的AbstractRefreshableConfigApplicationContext
 //设置 Bean 定义资源文件的定位路径
 //定位配置文件的位置
 /*
 public void setConfigLocation(String location) {
 //String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
 //即多个资源文件路径之间用” ,; /t/n”分隔,解析成数组形式
 //我们既可以使用一个字符串来配置多个 Spring Bean 定义资源文件,也可以使用字符串数组,即下面两种方式都是可以的:
 // ClasspathResource res = new ClasspathResource("a.xml,b.xml,……");
 setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
 } /**
 * 解析 Bean 定义资源文件的路径,处理多个资源文件字符串数组
 */
 public void setConfigLocations(@Nullable String... locations) {
 if (locations != null) {
 Assert.noNullElements(locations, "Config locations must not be null");
 this.configLocations = new String[locations.length];
 for (int i = 0; i < locations.length; i++) {
 // resolvePath 为同一个类中将字符串解析为路径的方法
 this.configLocations[i] = resolvePath(locations[i]).trim();
 }
 }
 else {
 this.configLocations = null;
 }
 }
 */
 setConfigLocations(configLocations);
 //刷新
 //通过调用其父类 AbstractApplicationContext 的 refresh()函数启动整个 IoC 容器对 Bean 定义的载入过程
 if (refresh) {
 refresh();
 }
 - } - //refresh()方法的作用是:在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在 refresh 之后使用的是新建立起来的 IOC 容器。refresh 的作用类似于对 IOC 容器的重启 
 /**
 // refresh()
 @Override
 public void refresh() throws BeansException, IllegalStateException {
 synchronized (this.startupShutdownMonitor) {
 // Prepare this context for refreshing.
 //调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
 prepareRefresh();- // Tell the subclass to refresh the internal bean factory.
 //告诉子类启动 refreshBeanFactory()方法,
 //Bean 定义资源文件的载入从子类的 refreshBeanFactory()方法启动
 /*
 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
 //这里使用了委派设计模式,父类定义了抽象的 refreshBeanFactory()方法,
 // protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
 // 具体实现调用子类容器的refreshBeanFactory()方法
 //查看call ctrl+alt+h
 /*
 * 先判断 BeanFactory 是否存在,如果存在则先销毁 beans 并关闭 beanFactory,
 * 接着创建 DefaultListableBeanFactory,并调用 loadBeanDefinitions(beanFactory)装载 bean 定义
 */
 @Override
 protected final void refreshBeanFactory() throws BeansException {
 //如果已经有容器,销毁容器中的 bean,关闭容器
 if (hasBeanFactory()) {
 destroyBeans();
 closeBeanFactory();
 }
 try {
 //创建 IOC 容器
 DefaultListableBeanFactory beanFactory = createBeanFactory();
 beanFactory.setSerializationId(getId());
 //对 IOC 容器进行定制化,如设置启动参数,开启注解的自动装配等
 customizeBeanFactory(beanFactory);
 //调用载入 Bean 定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的 loadBeanDefinitions 方法,具体的实现调用子类容器
 // protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
 // throws BeansException, IOException;
 //容器真正调用的是其子类 AbstractXmlApplicationContext 对该方法的实现
 /**
 //实现父类抽象的载入 Bean 定义方法
 @Override
 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
 // Create a new XmlBeanDefinitionReader for the given BeanFactory.
 //创建 XmlBeanDefinitionReader,即创建 Bean 读取器,并通过回调设置到容器中去,容 器使用该读取器读取 Bean 定义资源
 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's
 // resource loading environment.
 //为 Bean 读取器设置 Spring 资源加载器,
 //AbstractXmlApplicationContext 的祖先父类 AbstractApplicationContext 继承 DefaultResourceLoader,
 // 因此,容器本身也是一个资源加载
 beanDefinitionReader.setEnvironment(this.getEnvironment());
 beanDefinitionReader.setResourceLoader(this);
 //为 Bean 读取器设置 SAX xml 解析器
 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader,
 // then proceed with actually loading the bean definitions.
 //当 Bean 读取器读取 Bean 定义的 Xml 资源文件时,启用 Xml 的校验机制
 initBeanDefinitionReader(beanDefinitionReader);
 //Bean 读取器真正实现加载的方法
 /**
 * Xml Bean 读取器加载 Bean 定义资源
 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
 //获取 Bean 定义资源的定位
 /**
 * 这里又使用了一个委托模式,调用子类的获取 Bean 定义资源定位的方法
 * 该方法在 ClassPathXmlApplicationContext 中进行实现,
 * 对于我们举例分析源码的 FileSystemXmlApplicationContext 没有使用该方法 @Nullable
 protected Resource[] getConfigResources() {
 return null;
 }
 */
 Resource[] configResources = getConfigResources();
 if (configResources != null) {
 //Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 定义资源
 reader.loadBeanDefinitions(configResources);
 }
 //如果子类中获取的 Bean 定义资源定位为空,则获取 FileSystemXmlApplicationContext 构造方法中 setConfigLocations 方法设置的资源
 String[] configLocations = getConfigLocations();
 if (configLocations != null) {
 //Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 定义资源
 //在其抽象父类 AbstractBeanDefinitionReader 中定义了载入过程
 //具体看源码
 /**
 public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
 //获取在 IoC 容器初始化过程中设置的资源加载器
 ResourceLoader resourceLoader = getResourceLoader();
 if (resourceLoader == null) {
 throw new BeanDefinitionStoreException(
 "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
 } if (resourceLoader instanceof ResourcePatternResolver) {
 // Resource pattern matching available.
 try {
 //将指定位置的 Bean 定义资源文件解析为 Spring IOC 容器封装的资源
 // 加载多个指定位置的 Bean 定义资源文件
 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
 //委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
 int count = loadBeanDefinitions(resources);
 if (actualResources != null) {
 Collections.addAll(actualResources, resources);
 }
 if (logger.isTraceEnabled()) {
 logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
 }
 return count;
 }
 catch (IOException ex) {
 throw new BeanDefinitionStoreException(
 "Could not resolve bean definition resource pattern [" + location + "]", ex);
 }
 }
 else {
 // Can only load single resources by absolute URL.
 //将指定位置的 Bean 定义资源文件解析为 Spring IOC 容器封装的资源
 //加载单个指定位置的 Bean 定义资源文件 //1.调用资源加载器的获取资源方法 resourceLoader.getResource(location),获取到要加载的资源
 Resource resource = resourceLoader.getResource(location);
 //委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
 //2.真正执行加载功能是其子类 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法
 int count = loadBeanDefinitions(resource);
 if (actualResources != null) {
 actualResources.add(resource);
 }
 if (logger.isTraceEnabled()) {
 logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
 }
 return count;
 }
 }
 */
 reader.loadBeanDefinitions(configLocations);
 }
 }
 */
 loadBeanDefinitions(beanDefinitionReader);
 }
 */
 loadBeanDefinitions(beanFactory);
 synchronized (this.beanFactoryMonitor) {
 this.beanFactory = beanFactory;
 }
 }
 catch (IOException ex) {
 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
 }
 }
 */
 refreshBeanFactory();
 return getBeanFactory();
 }
 */
 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.
 //为容器的某些子类指定特殊的BeanPost事件处理器
 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
 //调用所有注册的 BeanFactoryPostProcessor 的 Bean
 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
 //为 BeanFactory 注册 BeanPost 事件处理器.
 //BeanPostProcessor 是 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.
 // 调用子类的某些特殊Bean初始化方法
 onRefresh(); // Check for listener beans and register them.
 // 为事件传播器注册事件监听器
 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
 //初始化所有剩余的单例 Bean.
 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.
 //销毁以创建的单态 Bean
 destroyBeans(); // Reset 'active' flag.
 //取消 refresh 操作,重置容器的同步标识.
 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();
 }
 }
 }
 - */ 
定位(定位查找配置文件) 通过ResourceLoader 得到一个Resource
加载() BeanDefinition 将XML形式定义的 Bean 定义资源文件转换为 Spring IOC 所识别的数据结构——BeanDefinition
注册() register 向 IOC 容器注册解析的 Bean
IOC容器实际就是一个map
IOC 容器的作用就是对这些注册的 Bean 定义信息进行处理和维护。这些的注册的 Bean 定义信息是 IoC 容器控制反转的基础,正是有了这些注册的数据,容器才可以进行依赖注入。
总结:
IOC 容器初始化的基本步骤:
- 初始化的入口在容器实现中的 refresh()调用来完成 
- 对 bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition, 
其中的大致过程如下:通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader 是默认的实现,
同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径,文件系统,URL 等方式来定为资源位置。
如果是 XmlBeanFactory 作为 IOC 容器,那么需要为它指定 bean 定义的资源,
也就是说 bean 定义文件时通过抽象成 Resource 来被 IOC 容器处理的,容器通过 BeanDefinitionReader 来完成定义信息的解析和 Bean 信息的注册,
往往使用的是 XmlBeanDefinitionReader 来解析 bean 的 xml 定义文件-实际的处理过程是委托给 BeanDefinitionParserDelegate 来完成的,
从而得到 bean 的定义信息,这些信息在 Spring 中使用 BeanDefinition 对象来表示-这个名字可以让我们想到 loadBeanDefinition,RegisterBeanDefinition 这些相关方法-他们都是为处理 BeanDefinition 服务的,
容器解析得到 BeanDefinitionIoC 以后,需要把它在 IOC 容器中注册,这由 IOC 实现BeanDefinitionRegistry 接口来实现。
注册过程就是在 IOC 容器内部维护的一个 HashMap 来保存得到的 BeanDefinition 的过程。
这个 HashMap 是 IOC 容器持有 bean 信息的场所,以后对 bean 的操作都是围绕这个 HashMap 来实现的.
- 然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 SpringIOC 的服务了,
 在使用 IOC容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IOC 风格编写的应用程序代码完全不用关心如何到达工厂,
 因为容器将把这些对象与容器管理的其他对象钩在一起。
 基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。
 Spring本身提供了对声明式载入 web 应用程序用法的应用程序上下文,并将其存储在 ServletContext 中的框架实现。
BeanFactory 和 FactoryBean
BeanFactory 和 FactoryBean,其中 BeanFactory 指的是 IOC 容器的编程抽象,比如ApplicationContext,XmlBeanFactory 等,这些都是 IOC 容器的具体表现,需要使用什么样的容器由客户决定,但 Spring 为我们提供了丰富的选择。FactoryBean 只是一个可以在 IOC 而容器中被管理的一个bean,是对各种处理过程和资源使用的抽象,FactoryBean 在需要时产生另一个对象,而不返回 FactoryBean 本身,我们可以把它看成是一个抽象工厂,对它的调用返回的是工厂生产的产品。所有的
FactoryBean 都实现特殊的 org.springframework.beans.factory.FactoryBean 接口,当使用容器中 FactoryBean 的时候,该容器不会返回 FactoryBean 本身,而是返回其生成的对象。Spring 包括了大部分的通用资源和服务访问抽象的 FactoryBean 的实现,其中包括:对 JNDI 查询的处理,对代理对象的处理,对事务性代理的处理,对 RMI 代理的处理等,这些我们都可以看成是具体的工厂,看成是 Spring 为我们建立好的工厂。也就是说 Spring 通过使用抽象工厂模式为我们准备了一系列工厂来生产一些特定的对象,免除我们手工重复的工作,我们要使用时只需要在 IOC 容器里配置好就能很方便的使用了
spring IOC简单分析的更多相关文章
- 1.Spring IoC简单例子
		Spring IoC简单例子 1.IHelloMessage.java package com.tony.spring.chapter01; public interface IHelloMessag ... 
- Spring IOC容器分析(2) -- BeanDefinition
		上文对Spring IOC容器的核心BeanFactory接口分析发现:在默认Bean工厂DefaultListableBeanFactory中对象不是以Object形成存储,而是以BeanDefin ... 
- Spring IOC容器分析(3) -- DefaultListableBeanFactory
		上一节介绍了封装bean对象的BeanDefinition接口.从前面小结对BeanFactory的介绍中,我们知道bean对象是存储在map中,通过调用getBean方法可以得到bean对象.在接口 ... 
- Spring IOC容器分析(4) -- bean创建获取完整流程
		上节探讨了Spring IOC容器中getBean方法,下面我们将自行编写测试用例,深入跟踪分析bean对象创建过程. 测试环境创建 测试示例代码如下: package org.springframe ... 
- Spring IOC原理分析
		IOC IOC(Inversion of Control)控制反转:所谓的控制反转,就是把原先需要我们代码自己实现对象的创建和依赖,反转给容器来实现.那么必然Spring需要创建一个容器,同时需要创建 ... 
- spring  IOC简单入门
		spring的核心是ioc和aop 先介绍一下IOC(inverse of control控制反转)又叫DI(Dependency injection依赖注入) 个人理解为把对象的控制权由类转移到配置 ... 
- Spring IOC容器分析(1) -- BeanFactory
		搭建好源码阅读环境后,就可以慢慢走进Spring殿堂了.IOC是Inversion of Control的缩写,控制反转的意思.很多人可能都知道IOC是spring的核心,将对象的创建初始化等权限交由 ... 
- Spring IOC初始化深度解析
		1.前言 本文是基于JAVA配置方法对Spring IOC进行分析,掌握Spring IOC初始化流程对于我们更好的使用Spring.学习Spring还是很有帮助的,本文所使用的的Spring版本为5 ... 
- [Spring] IOC - study
		Spring IOC简单注入例子,本例子使用JUnit进行测试.Spring版本:3.2 项目结构: Spring所需引用的JAR包: Spring XML配置: springContext.xml ... 
随机推荐
- LY.JAVA面向对象编程.工具类中使用静态、说明书的制作过程、API文档的使用过程
			2018-07-08 获取数组中的最大值 某个数字在数组中第一次出现时的索引 制作说明书的过程 对工具类的使用 获取数组中的最大值 获取数字在数组中第一次出现的索引值 API的使用过程 Math 
- C++解析七-重载运算符和重载函数
			重载运算符和重载函数C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载.重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列 ... 
- Git的基本使用(github)
			关于Git的基本使用: 上传本地文件到github仓库中 首先要有自己的github账号,新建仓库: saiku-3.9 其次 本地安装好 git , 在本地任意目录下新建目录 saiku-3.9, ... 
- maven 打包zip,jsw相关
			参考链接: https://blog.csdn.net/masson32/article/details/51802732 
- Linux搭建Hadoop集群---Jdk配置
			三台虚拟机:master slave1 slave2 192.168.77.99 master 192.168.77.88 slave1 192.168.77.77 slave2 1.修改主机名: ... 
- CAN总线(1)--初探(更新中)
			前言: CAN总线可以控制可以使用Xilinx中IP核来直接实现,也可以使用专用的CAN芯片(例如:SJA1000)通过单片机和FPGA驱动控制来实现: 目前是使用控制器SJA1000来进行实现: C ... 
- Docker Kubernetes(K8s)简介
			入职了新公司,使用了Docker和K8s,需要有一个基础的了解,对网络上相关信息进行了简单总结. 一Docker 1简介: Docker 将应用程序与该程序的依赖,打包在一个文件里面.运行这个文件,就 ... 
- ps基础学习笔记一
			图像?表示分为位图方式和矢量图方式 位图是像素点组成,一副图像所含像素越多,图像的效果就越好 矢量图是基于一定数学方式描述,适合表示色彩较少,一色块为主,曲线简单的图像,文件小ps一般用来处理位图,c ... 
- Docker(2):快速入门及常用命令
			什么是Docker? Docker 是世界领先的软件容器平台.开发人员利用 Docker 可以消除协作编码时“在我的机器上可正常工作”的问题.运维人员利用 Docker 可以在隔离容器中并行运行和管理 ... 
- marquee 的浏览器兼容性
			marquee 在IE,firefox,chrome ,safari下都能正常的实现走马灯效果,兼容性没有问题 并且两个关键属性scrollamount(滚动速度)direction(滚动方向) 所有 ... 
