
主要讲解IOC和DI理解、BeanFactory和ApplicationContext、Bean生命周期和加载过程、BeanFactory和FactoryBean区别基本包括IOC的所有重要基础知识。温馨提示全文接近1W字感兴趣的同学可以收藏起来慢慢看。前言这两天一直在学习Spring我把Spring分成两部分来学分别为IOC和AOP然后IOC我又分为纯理论知识和常用注解的使用姿势这篇文章主要讲IOC的纯理论部分所以看起来可能会有点枯燥但是仔细体味一下感觉里面还是有点意思。这些纯理论知识基本都是来自于网络博客我也不能再去看代码然后重复造轮子但是网上的示例我肯定会自己亲自运行一遍才会贴出来。然后注解的使用我也会自己使用完后才会去讲解使用的方法。至于IOC的常用注解之前在文章《【Spring基础系列1】基于注解装配Bean》写过一部分下一篇文章会再完善另外一部分。谈谈对Spring IOC的理解学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念对于初学Spring的人来说总觉得IOC、DI这两个概念是模糊不清的然后网上讲解的也五花八门我看了很多相关的博客发现有一些博客总结的很到位下面就分享2位对这两个概念的理解应该是我看过的讲解最到位的2种。分享Iteye的开涛对Ioc的精彩讲解首先要分享的是Iteye的开涛这位技术牛人对Spring框架的IOC的理解写得非常通俗易懂以下内容全部来自原文原文地址http://jinnianshilongnian.iteye.com/blog/1413846IoC是什么Ioc—Inversion of Control即“控制反转”不是什么技术而是一种设计思想。在Java开发中Ioc意味着将你设计好的对象交给容器控制而不是传统的在你的对象内部直接控制。如何理解好Ioc呢理解好Ioc的关键是要明确“谁控制谁控制什么为何是反转有反转就应该有正转了哪些方面反转了”那我们来深入分析一下谁控制谁控制什么传统Java SE程序设计我们直接在对象内部通过new进行创建对象是程序主动去创建依赖对象而IoC是有专门一个容器来创建这些对象即由Ioc容器来控制对 象的创建谁控制谁当然是IoC 容器控制了对象控制什么那就是主要控制了外部资源获取不只是对象包括比如文件等。为何是反转哪些方面反转了有反转就有正转传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象也就是正转而反转则是由容器来帮忙创建及注入依赖对象为何是反转因为由容器帮我们查找及注入依赖对象对象只是被动的接受依赖对象所以是反转哪些方面反转了依赖对象的获取被反转了。用图例说明一下传统程序设计如图都是主动去创建相关对象然后再组合起来当有了IoC/DI的容器后在客户端类中不再主动去创建这些对象了如图所示:IoC能做什么IoC 不是一种技术只是一种思想一个重要的面向对象编程的法则它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象从而导致类与类之间高耦合难于测试有了IoC容器后把创建和查找依赖对象的控制权交给了容器由容器进行注入组合对象所以对象与对象之间是 松散耦合这样也方便测试利于功能复用更重要的是使得程序的整个体系结构变得非常灵活。其实IoC对编程带来的最大改变不是从代码上而是从思想上发生了“主从换位”的变化。应用程序原本是老大要获取什么资源都是主动出击但是在IoC/DI思想中应用程序就变成被动的了被动的等待IoC容器来创建并注入它所需要的资源了。IoC很好的体现了面向对象设计法则之一—— 好莱坞法则“别找我们我们找你”即由IoC容器帮对象找相应的依赖对象并注入而不是由对象主动去找。IoC和DIDI—Dependency Injection即“依赖注入”组件之间依赖关系由容器在运行期决定形象的说即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能而是为了提升组件重用的频率并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制我们只需要通过简单的配置而无需任何代码就可指定目标需要的资源完成自身的业务逻辑而不需要关心具体的资源来自何处由谁实现。理解DI的关键是“谁依赖谁为什么需要依赖谁注入谁注入了什么”那我们来深入分析一下谁依赖于谁当然是应用程序依赖于IoC容器为什么需要依赖应用程序需要IoC容器来提供对象需要的外部资源谁注入谁很明显是IoC容器注入应用程序某个对象应用程序依赖的对象注入了什么就是注入某个对象所需要的外部资源包括对象、资源、常量数据。IoC和DI由什么关系呢其实它们是同一个概念的不同角度描述由于控制反转概念比较含糊可能只是理解为容器控制对象这一个层面很难让人想到谁来维护对象关系所以2004年大师级人物Martin Fowler又给出了一个新的名字“依赖注入”相对IoC 而言“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。看过很多对Spring的Ioc理解的文章好多人对Ioc和DI的解释都晦涩难懂反正就是一种说不清道不明的感觉读完之后依然是一头雾水感觉就是开涛这位技术牛人写得特别通俗易懂他清楚地解释了IoC(控制反转) 和DI(依赖注入)中的每一个字读完之后给人一种豁然开朗的感觉。我相信对于初学Spring框架的人对Ioc的理解应该是有很大帮助的。分享Bromon的blog上对IoC与DI的讲解IoC(控制反转)首先想说说IoCInversion of Control控制反转。这是spring的核心贯穿始终。所谓IoC对于spring框架来说就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢举个简单的例子我们是如何找女朋友的常见的情况是我们到处去看哪里有长得漂亮身材又好的mm然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………想办法认识她们投其所好送其所要然后嘿嘿……这个过程是复杂深奥的我们必须自己设计和面对每个环节。传统的程序开发也是如此在一个对象中如果要使用另外的对象就必须得到它自己new一个或者从JNDI中查询一个使用完之后还要将对象销毁比如Connection等对象始终会和其他的接口或类藕合起来。那么IoC是如何做的呢有点像通过婚介找女朋友在我和女朋友之间引入了一个第三者婚姻介绍所。婚介管理了很多男男女女的资料我可以向婚介提出一个列表告诉它我想找个什么样的女朋友比如长得像李嘉欣身材像林熙雷唱歌像周杰伦速度像卡洛斯技术像齐达内之类的然后婚介就会按照我们的要求提供一个mm我们只需要去和她谈恋爱、结婚就行了。简单明了如果婚介给我们的人选不符合要求我们就会抛出异常。整个过程不再由我自己控制而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此所有的类都会在spring容器中登记告诉spring你是个什么东西你需要什么东西然后spring会在系统运行到适当的时候把你要的东西主动给你同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制也就是说控制对象生存周期的不再是引用它的对象而是spring。对于某个具体的对象而言以前是它控制其他对象现在是所有对象都被spring控制所以这叫控制反转。DI(依赖注入)IoC的一个重点是在系统运行中动态的向某个对象提供它所需要的其他对象。这一点是通过DIDependency Injection依赖注入来实现的。比如对象A需要操作数据库以前我们总是要在A中自己编写代码来获得一个Connection对象有了 spring我们就只需要告诉springA中需要一个Connection至于这个Connection怎么构造何时构造A不需要知道。在系统运行时spring会在适当的时候制造一个Connection然后像打针一样注射到A当中这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行而这个Connection是由spring注入到A中的依赖注入的名字就这么来的。那么DI是如何实现的呢Java 1.3之后一个重要特征是反射reflection它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性spring就是通过反射来实现注入的。理解了IoC和DI的概念后一切都将变得简单明了剩下的工作只是在spring的框架中堆积木而已。Spring IOC原理总结依赖注入流程这个博主通过磨咖啡的故事生动讲解了注入流程将生产咖啡总结为两个阶段采摘和收集“咖啡豆”bean研磨和烹饪咖啡阶段一收集和注册第一阶段可以认为是构建和收集bean定义的阶段在这个阶段中我们可以通过XML或者Java代码的方式定义一些bean然后通过手动组装或者让容器基于某些机制自动扫描的形式将这些bean定义收集到IOC容器中。假设我们以XML配置的形式来收集并注册单一bean一般形式如下bean idmockService class..MockServiceImpl ... /bean如果嫌逐个收集bean定义麻烦想批量地收集并注册到IOC容器中我们也可以通过XML Schema形式的配置进行批量扫描并采集和注册context:component-scan base-packagecom.keevol阶段二分析和组装当第一阶段工作完成后我们可以先暂且认为IOC容器中充斥着一个个独立的bean它们之间没有任何关系。但实际上它们之间是有依赖关系的所以IOC容器在第二阶段要干的事情就是分析这些已经在IOC容器之中的bean然后根据它们之间的依赖关系先后组装它们。如果IOC容器发现某个bean依赖另一个bean它就会将这另一个bean注入给依赖它的那个bean直到所有到bean的依赖都注入完成所有bean都“整装待发”整个IOC容器都工作即算完成。至于分析和组装的依据Spring框架最早是通过XML配置文件的形式来描述bean与bean之间的关系随着Java业界研发技术和理念都转变基于Java代码和Annotation元信息的描述方式也日渐兴盛比如Autowired和Inject但不管使用哪种方式都只是为了简化绑定逻辑描述的各种“表象”最终都是为本阶段都最终目的服务。这个讲解只是给大家对IOC有一个初步的认识下面才是核心的部分。IOC容器的原理IOC容器其实就是一个大工厂它用来管理我们所有的对象以及依赖关系。原理就是通过Java的反射技术来实现的通过反射我们可以获取类的所有信息(成员变量、类名等等等)再通过配置文件(xml)或者注解来描述类与类之间的关系我们就可以通过这些配置信息和反射技术来构建出对应的对象和依赖关系了上面描述的技术只要学过点Java的都能说出来我们简单来看看实际Spring IOC容器是怎么实现对象的创建和依赖的根据Bean配置信息在容器内部创建Bean定义注册表根据注册表加载、实例化bean、建立Bean与Bean之间的依赖关系将这些准备就绪的Bean放到Map缓存池中等待应用程序调用Spring容器(Bean工厂)可简单分成两种BeanFactory这是最基础、面向Spring的ApplicationContext这是在BeanFactory基础之上面向使用Spring框架的开发者。提供了一系列的功能几乎所有的应用场合都是使用ApplicationContextBeanFactory vs ApplicationContextBeanFactoryBeanFactory 是 Spring 的“心脏”。它就是 Spring IoC 容器的真面目。Spring 使用 BeanFactory 来实例化、配置和管理 Bean。BeanFactory是IOC容器的核心接口 它定义了IOC的基本功能我们看到它主要定义了getBean方法。getBean方法是IOC容器获取bean对象和引发依赖注入的起点。方法的功能是返回特定的名称的Bean。BeanFactory 是初始化 Bean 和调用它们生命周期方法的“吃苦耐劳者”。注意BeanFactory 只能管理单例SingletonBean 的生命周期。它不能管理原型(prototype,非单例)Bean 的生命周期。这是因为原型 Bean 实例被创建之后便被传给了客户端,容器失去了对它们的引用。BeanFactory有着庞大的继承、实现体系有众多的子接口、实现类。来看一下BeanFactory的基本类体系结构接口为主下面写了一大堆我觉得仅供了解即可BeanFactory作为一个主接口不继承任何接口暂且称为一级接口。有3个子接口继承了它进行功能上的增强。这3个子接口称为二级接口。ConfigurableBeanFactory可以被称为三级接口对二级接口HierarchicalBeanFactory进行了再次增强它还继承了另一个外来的接口SingletonBeanRegistryConfigurableListableBeanFactory是一个更强大的接口继承了上述的所有接口无所不包称为四级接口。 这4级接口是BeanFactory的基本接口体系。继续下面是继承关系的2个抽象类和2个实现类AbstractBeanFactory作为一个抽象类实现了三级接口ConfigurableBeanFactory大部分功能。AbstractAutowireCapableBeanFactory同样是抽象类继承自AbstractBeanFactory并额外实现了二级接口AutowireCapableBeanFactoryDefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory实现了最强大的四级接口ConfigurableListableBeanFactory并实现了一个外来接口BeanDefinitionRegistry它并非抽象类。最后是最强大的XmlBeanFactory继承自DefaultListableBeanFactory重写了一些功能使自己更强大。再来看一下BeanFactory的源码public interface BeanFactory { /** * 用来引用一个实例或把它和工厂产生的Bean区分开就是说如果一个FactoryBean的名字为a那么a会得到那个Factory */ String FACTORY_BEAN_PREFIX ; /* * 四个不同形式的getBean方法获取实例 */ Object getBean(String name) throws BeansException; T T getBean(String name, ClassT requiredType) throws BeansException; T T getBean(ClassT requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; boolean containsBean(String name); // 是否存在 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否为单实例 boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否为原型多实例 boolean isTypeMatch(String name, Class? targetType) throws NoSuchBeanDefinitionException;// 名称、类型是否匹配 Class? getType(String name) throws NoSuchBeanDefinitionException; // 获取类型 String[] getAliases(String name);// 根据实例的名字获取实例的别名 }具体4个获取实例的方法。getBean的重载方法。4个判断的方法。判断是否存在是否为单例、原型名称类型是否匹配。1个获取类型的方法、一个获取别名的方法。根据名称获取类型、根据名称获取别名。一目了然!总结这10个方法很明显这是一个典型的工厂模式的工厂接口。看一个简单的示例Data public class Cat implements Animal { private String catName 罗小黑; } Data public class Pets { Resource private Cat cat; public static void main(String args[]) { ResourcePatternResolver resolver new PathMatchingResourcePatternResolver(); org.springframework.core.io.Resource res resolver.getResource(classpath:applicationContext.xml); BeanFactory factory new XmlBeanFactory(res); Pets petsfactory.getBean(pets, Pets.class); System.out.println(pets.toString()); } } // 输出 // Pets(catCat(catName罗小黑))这里需要到applicationContext.xml添加一下配置bean idpets classcom.java.annotation.spring.bean.test4.Pets property namecat refcat / /bean bean idcat classcom.java.annotation.spring.bean.test4.Cat /解读一下XmlBeanFactory通过Resource装载Spring配置信息冰启动IoC容器然后就可以通过factory.getBean从IoC容器中获取Bean了。通过BeanFactory启动IoC容器时并不会初始化配置文件中定义的Bean初始化动作发生在第一个调用时。对于单实例singleton的Bean来说BeanFactory会缓存Bean实例所以第二次使用getBean时直接从IoC容器缓存中获取Bean。这种方式非常不建议使用因为我用这种方式发现添加的Service注解不能生效.ApplicationContext如果说BeanFactory是Spring的心脏那么ApplicationContext就是完整的躯体了ApplicationContext由BeanFactory派生而来提供了更多面向实际应用的功能。在BeanFactory中很多功能需要以编程的方式实现而在ApplicationContext中则可以通过配置实现。BeanFactorty接口提供了配置框架及基本功能但是无法支持spring的aop功能和web应用。而ApplicationContext接口作为BeanFactory的派生因而提供BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展相较于BeanFactortyApplicationContext还提供了以下的功能MessageSource, 提供国际化的消息访问资源访问如URL和文件事件传播特性即支持aop特性载入多个有继承关系上下文 使得每一个上下文都专注于一个特定的层次比如应用的web层ApplicationContext是IOC容器另一个重要接口 它继承了BeanFactory的基本功能 同时也继承了容器的高级功能如MessageSource国际化资源接口、ResourceLoader资源加载接口、ApplicationEventPublisher应用事件发布接口等。ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口在此基础上还通过多个其他的接口扩展了 BeanFactory 的功能下面都是八股文也是仅作了解即可ClassPathXmlApplicationContext默认从类路径加载配置文件FileSystemXmlApplicationContext默认从文件系统中装载配置文件 ApplicationEventPublisher让容器拥有发布应用上下文事件的功能包括容器启动事件、关闭事件等。实现了 ApplicationListener 事件监听接口的 Bean 可以接收到容器事件 并对事件进行响应处理 。在 ApplicationContext 抽象实现类AbstractApplicationContext 中我们可以发现存在一个 ApplicationEventMulticaster它负责保存所有监听器以便在容器产生上下文事件时通知这些事件监听者。MessageSource为应用提供 i18n 国际化消息访问的功能ResourcePatternResolver 所 有 ApplicationContext 实现类都实现了类似于PathMatchingResourcePatternResolver 的功能可以通过带前缀的 Ant 风格的资源文件路径装载 Spring 的配置文件。LifeCycle该接口是 Spring 2.0 加入的该接口提供了 start()和 stop()两个方法主要用于控制异步处理过程。在具体使用时该接口同时被 ApplicationContext 实现及具体 Bean 实现 ApplicationContext 会将 start/stop 的信息传递给容器中所有实现了该接口的 Bean以达到管理和控制 JMX、任务调度等目的。ConfigurableApplicationContext 扩展于 ApplicationContext它新增加了两个主要的方法refresh()和 close()让 ApplicationContext 具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用 refresh()即可启动应用上下文在已经启动的状态下调用 refresh()则清除缓存并重新装载配置信息而调用close()则可关闭应用上下文。这些接口方法为容器的控制管理带来了便利但作为开发者我们并不需要过多关心这些方法。还是看一下ClassPathXmlApplicationContext的使用姿势ApplicationContext context new ClassPathXmlApplicationContext(classpath:applicationContext.xml); //ApplicationContext context new ClassPathXmlApplicationContext(file:/Users/mengloulv/java-workspace/Demo5/src/main/resources/applicationContext.xml); Pets petscontext.getBean(pets, Pets.class); System.out.println(pets.toString());再看一下FileSystemXmlApplicationContext的使用姿势ApplicationContext context new FileSystemXmlApplicationContext(file:/Users/mengloulv/java-workspace/Demo5/src/main/resources/applicationContext.xml); Pets petscontext.getBean(pets, Pets.class); System.out.println(pets.toString());个人还是偏向使用ClassPathXmlApplicationContext可以使用相对路径。后面还有个WebApplicationContext反正我是用的少就不介绍了。BeanFactory和ApplicationContext的区别1.BeanFactroy采用的是延迟加载形式来注入Bean的即只有在使用到某个Bean时(调用getBean())才对该Bean进行加载实例化这样我们就不能发现一些存在的Spring的配置问题。而ApplicationContext则相反它是在容器启动时一次性创建了所有的Bean。这样在容器启动时我们就可以发现Spring中存在的配置错误。相对于基本的BeanFactoryApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时程序启动较慢。BeanFacotry延迟加载,如果Bean的某一个属性没有注入BeanFacotry加载后直至第一次使用调用getBean方法才会抛出异常而ApplicationContext则在初始化自身是检验这样有利于检查所依赖属性是否注入所以通常情况下我们选择使用 ApplicationContext。应用上下文则会在上下文启动后预载入所有的单实例Bean。通过预载入单实例bean ,确保当你需要的时候你就不用等待因为它们已经创建好了。2.BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用但两者之间的区别是BeanFactory需要手动注册而ApplicationContext则是自动注册。Applicationcontext比 beanFactory 加入了一些更好使用的功能。而且 beanFactory 的许多功能需要通过编程实现而 Applicationcontext 可以通过配置实现。比如后处理 bean Applicationcontext 直接配置在配置文件即可而 beanFactory 这要在代码中显示的写出来才可以被容器识别。3.beanFactory主要是面对与 spring 框架的基础设施面对 spring 自己。而 Applicationcontex 主要面对与 spring 使用的开发者。基本都会使用 Applicationcontex 并非 beanFactory 。Bean的生命周期加载过程Bean的生命周期看了上面那么多内容是不是基本快完了NO,NO,NO,重要的部分在这里当调用者通过 getBean(beanName)向容器请求某一个 Bean 时如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor 接口在实例化 Bean 之前将调用接口的 postProcessBeforeInstantiation()方法根据配置情况调用 Bean 构造函数或工厂方法实例化 Bean如果容器注册了 InstantiationAwareBeanPostProcessor 接口在实例化 Bean 之后调用该接口的 postProcessAfterInstantiation()方法可在这里对已经实例化的对象进行一些“梳妆打扮”如果 Bean 配置了属性信息容器在这一步着手将配置值设置到 Bean 对应的属性中不过在设置每个属性之前将先调用InstantiationAwareBeanPostProcessor 接口的postProcessPropertyValues()方法调用 Bean 的属性设置方法设置属性值如果 Bean 实现了 org.springframework.beans.factory.BeanNameAware 接口将调用setBeanName()接口方法将配置文件中该 Bean 对应的名称设置到 Bean 中如果 Bean 实现了 org.springframework.beans.factory.BeanFactoryAware 接口将调用 setBeanFactory()接口方法将 BeanFactory 容器实例设置到 Bean 中如果 BeanFactory 装配了 org.springframework.beans.factory.config.BeanPostProcessor后处理器将调用 BeanPostProcessor 的 Object postProcessBeforeInitialization(Object bean, String beanName)接口方法对 Bean 进行加工操作。其中入参 bean 是当前正在处理的 Bean而 beanName 是当前 Bean 的配置名返回的对象为加工处理后的 Bean。用户可以使用该方法对某些 Bean 进行特殊的处理甚至改变 Bean 的行为 BeanPostProcessor 在 Spring 框架中占有重要的地位为容器提供对 Bean 进行后续加工处理的切入点 Spring 容器所提供的各种“神奇功能”如 AOP动态代理等都通过 BeanPostProcessor 实施如果 Bean 实现了 InitializingBean 的接口将调用接口的 afterPropertiesSet()方法如果在通过 init-method 属性定义了初始化方法将执行这个方法BeanPostProcessor 后处理器定义了两个方法其一是 postProcessBeforeInitialization() 在第 8 步调用其二是 Object postProcessAfterInitialization(Object bean, String beanName)方法这个方法在此时调用容器再次获得对 Bean 进行加工处理的机会如果在中指定 Bean 的作用范围为 scope“prototype”将 Bean 返回给调用者调用者负责 Bean 后续生命的管理 Spring 不再管理这个 Bean 的生命周期。如果作用范围设置为 scope“singleton”则将 Bean 放入到 Spring IoC 容器的缓存池中并将 Bean引用返回给调用者 Spring 继续对这些 Bean 进行后续的生命管理对于 scope“singleton”的 Bean当容器关闭时将触发 Spring 对 Bean 的后续生命周期的管理工作首先如果 Bean 实现了 DisposableBean 接口则将调用接口的afterPropertiesSet()方法可以在此编写释放资源、记录日志等操作对于 scope“singleton”的 Bean如果通过的 destroy-method 属性指定了 Bean 的销毁方法 Spring 将执行 Bean 的这个方法完成 Bean 资源的释放等操作。可以将这些方法大致划分为三类Bean 自身的方法如调用 Bean 构造函数实例化 Bean调用 Setter 设置 Bean 的属性值以及通过的 init-method 和 destroy-method 所指定的方法Bean 级生命周期接口方法如 BeanNameAware、 BeanFactoryAware、 InitializingBean 和 DisposableBean这些接口方法由 Bean 类直接实现容器级生命周期接口方法在上图中带“★” 的步骤是由 InstantiationAwareBean PostProcessor 和 BeanPostProcessor 这两个接口实现一般称它们的实现类为“ 后处理器” 。后处理器接口一般不由 Bean 本身实现它们独立于 Bean实现类以容器附加装置的形式注册到 Spring 容器中并通过接口反射为 Spring 容器预先识别。当Spring 容器创建任何 Bean 的时候这些后处理器都会发生作用所以这些后处理器的影响是全局性的。当然用户可以通过合理地编写后处理器让其仅对感兴趣Bean 进行加工处理ApplicationContext 和 BeanFactory 另一个最大的不同之处在于ApplicationContext会利用 Java 反射机制自动识别出配置文件中定义的 BeanPostProcessor、 InstantiationAwareBeanPostProcessor 和 BeanFactoryPostProcessor并自动将它们注册到应用上下文中而后者需要在代码中通过手工调用 addBeanPostProcessor()方法进行注册。这也是为什么在应用开发时我们普遍使用 ApplicationContext 而很少使用 BeanFactory 的原因之一。这是对Bean的生命周期解释的最详细的一篇博文没有之一这个八股文就需要多看几遍虽然有些地方还不能完全理解我打算后面再看看源码应该就能理解了。Bean加载过程Spring的高明之处在于它使用众多接口描绘出了所有装置的蓝图构建好Spring的骨架继而通过继承体系层层推演不断丰富最终让Spring成为有血有肉的完整的框架。所以查看Spring框架的源码时有两条清晰可见的脉络接口层描述了容器的重要组件及组件间的协作关系继承体系逐步实现组件的各项功能。接口层清晰地勾勒出Spring框架的高层功能框架脉络呼之欲出。有了接口层抽象的描述后不但Spring自己可以提供具体的实现任何第三方组织也可以提供不同实现 可以说Spring完善的接口层使框架的扩展性得到了很好的保证。纵向继承体系的逐步扩展分步骤地实现框架的功能这种实现方案保证了框架功能不会堆积在某些类的身上造成过重的代码逻辑负载框架的复杂度被完美地分解开了。Spring组件按其所承担的角色可以划分为两类物料组件Resource、BeanDefinition、PropertyEditor以及最终的Bean等它们是加工流程中被加工、被消费的组件就像流水线上被加工的物料BeanDefinitionSpring通过BeanDefinition将配置文件中的配置信息转换为容器的内部表示并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的后续操作直接从BeanDefinitionRegistry中读取配置信息。加工设备组件ResourceLoader、BeanDefinitionReader、BeanFactoryPostProcessor、InstantiationStrategy以及BeanWrapper等组件像是流水线上不同环节的加工设备对物料组件进行加工处理。InstantiationStrategy负责实例化Bean操作相当于Java语言中new的功能并不会参与Bean属性的配置工作。属性填充工作留待BeanWrapper完成BeanWrapper继承了PropertyAccessor和PropertyEditorRegistry接口BeanWrapperImpl内部封装了两类组件1被封装的目标Bean2一套用于设置Bean属性的属性编辑器具有三重身份1Bean包裹器2属性访问器 3属性编辑器注册表。PropertyAccessor定义了各种访问Bean属性的方法。PropertyEditorRegistry属性编辑器的注册表。该图描述了Spring容器从加载配置文件到创建出一个完整Bean的作业流程ResourceLoader从存储介质中加载Spring配置信息并使用Resource表示这个配置文件的资源BeanDefinitionReader读取Resource所指向的配置文件资源然后解析配置文件。配置文件中每一个解析成一个BeanDefinition对象并保存到BeanDefinitionRegistry中容器扫描BeanDefinitionRegistry中的BeanDefinition使用Java的反射机制自动识别出Bean工厂后处理后器实现BeanFactoryPostProcessor接口的Bean然后调用这些Bean工厂后处理器对BeanDefinitionRegistry中的BeanDefinition进行加工处理。主要完成以下两项工作对使用到占位符的元素标签进行解析得到最终的配置值这意味对一些半成品式的BeanDefinition对象进行加工处理并得到成品的BeanDefinition对象对BeanDefinitionRegistry中的BeanDefinition进行扫描通过Java反射机制找出所有属性编辑器的Bean实现java.beans.PropertyEditor接口的Bean并自动将它们注册到Spring容器的属性编辑器注册表中PropertyEditorRegistrySpring容器从BeanDefinitionRegistry中取出加工后的BeanDefinition并调用InstantiationStrategy着手进行Bean实例化的工作在实例化Bean时Spring容器使用BeanWrapper对Bean进行封装BeanWrapper提供了很多以Java反射机制操作Bean的方法它将结合该Bean的BeanDefinition以及容器中属性编辑器完成Bean属性的设置工作利用容器中注册的Bean后处理器实现BeanPostProcessor接口的Bean对已经完成属性设置工作的Bean进行后续加工直接装配出一个准备就绪的Bean。小结一下Spring IOC容器主要有继承体系底层的BeanFactory、高层的ApplicationContext和WebApplicationContext。Bean有自己的生命周期容器加载Bean原理BeanDefinitionReader读取Resource所指向的配置文件资源然后解析配置文件。配置文件中每一个解析成一个BeanDefinition对象并保存到BeanDefinitionRegistry中容器扫描BeanDefinitionRegistry中的BeanDefinition调用InstantiationStrategy进行Bean实例化的工作使用BeanWrapper完成Bean属性的设置工作单例Bean缓存池Spring 在 DefaultSingletonBeanRegistry 类中提供了一个用于缓存单实例 Bean 的缓存器它是一个用 HashMap 实现的缓存器单实例的 Bean 以 beanName 为键保存在这个HashMap 中。BeanFacotry vs FactoryBean这个面试经常会问到然后对这块我觉得也有点意思就作为基础内容的一部分。两者区别BeanFactory是接口提供了OC容器最基本的形式给具体的IOC容器的实现提供了规范FactoryBean也是接口为IOC容器中Bean的实现提供了更加灵活的方式FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式我们可以在getObject()方法中灵活配置。其实在Spring源码中有很多FactoryBean的实现类。区别BeanFactory是个Factory也就是IOC容器或对象工厂FactoryBean是个Bean。在Spring中所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言这个Bean不是简单的Bean而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。BeanFactory回顾一下BeanFactory以Factory结尾表示它是一个工厂类(接口) 它负责生产和管理bean的一个工厂。在Spring中BeanFactory是IOC容器的核心接口它的职责包括实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口并不是IOC容器的具体实现但是Spring容器给出了很多种实现如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等其中XmlBeanFactory就是常用的一个该实现将以XML方式描述组成应用的对象及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据并用它来构建一个完全可配置的系统或应用。更详细的内容直接看文章的”BeanFactory“部分。FactoryBean一般情况下Spring通过反射机制利用的class属性指定实现类实例化Bean在某些情况下实例化Bean过程比较复杂如果按照传统的方式则需要在中提供大量的配置信息。配置方式的灵活性是受限的这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个FactoryBean的工厂类接口用户可以通过实现该接口定制实例化Bean的逻辑。Spring自身就提供了70多个FactoryBean的实现它们隐藏了实例化一些复杂Bean的细节给上层应用带来了便利。接口中定义的方法如下:public interface FactoryBeanT { /** * 返回对象的实例 */ T getObject() throws Exception; /** * 返回对象的类型 */ Class? getObjectType(); /** * 是否是单例 */ boolean isSingleton(); }最好的学习就是动手实践一下我们看一下spring如何通过Factorybean配置Bean我们先定义一个car对象Data public class Car { private int maxSpeed; private String brand; private double price; }在定义一个CarFactoryBean用来包装Car实例public class CarFactoryBean implements FactoryBeanCar { private String carInfo ; /** * 自定义创建bean的过程可以定制复杂的创建过程 */ Override public Car getObject() throws Exception { Car car new Car(); String[] infos carInfo.split(,); car.setBrand(infos[0]); car.setMaxSpeed(Integer.valueOf(infos[1])); car.setPrice(Double.valueOf(infos[2])); return car; } /** * 获取FactoryBean创建bean的类型 */ Override public Class? getObjectType() { return Car.class; } /** * 创建的bean是否是单例 */ Override public boolean isSingleton() { return false; } public String getCarInfo() { return carInfo; } /** * 接受逗号分隔符设置属性 */ public void setCarInfo(String carInfo) { this.carInfo carInfo; } }这里是重点需要添加如下配置bean idcar classcom.java.spring.factorybean2.CarFactoryBean property namecarInfo value法拉利跑车,420,2600000/ /bean最后测试用例如下public class MyFactoryBeanTest { /** * 测试验证FactoryBean原理代理一个servcie在调用其方法的前后打印日志亦可作其他处理 * 从ApplicationContext中获取自定义的FactoryBean * context.getBean(String beanName) --- 最终获取到的Object是FactoryBean.getObejct(), * 使用Proxy.newInstance生成service的代理类 */ Test public void testFactoryBean() { ApplicationContext context new ClassPathXmlApplicationContext(classpath:applicationContext.xml); Car car (Car) context.getBean(car); System.out.println(car.toString()); } } // 输出 // Car(maxSpeed420, brand法拉利跑车, price2600000.0)测试的时候我还特意打了个断点确实会进入getObject()中网上本来找了2个示例但是有一个一直跑步起来然后这个示例我找了N多个相关的博客给出的示例都是缺胳膊少腿完全不能运行倒腾了大半个小时终于倒腾出一个能运行的完整版本。