在理解任何技术之前,我都会问自己一个问题:它的产生是为了解决什么样的问题,以及如何解决这些问题?希望你能在本篇文章中找到答案……
(由于大家对Ioc应该是经常使用了,所以这里不会告诉你应该怎么样使用,重要的是理解思想原理,理解过程)

一、IOC的概念

IoC可以说是spring最核心的部分,是spring家族任意组件的基本。Ioc 本身并不能算为一种技术,而是一种思想,它使你从繁琐的对象交互中解脱出来,而专注于对象本身,更进一步突出面向对象。
我们先来回答文章开头问题的上半部分:
我们假设一个场景:Person(人)每天都要吃早餐(食物)。我们可以用如下程序表示

public class Person {public void eat() {Food food = new food();System.out.println("I eat food:{}", food.toString());}
}复制代码

在我们吃饭之前必须先new food()(做饭),要不然就吃不上。
Ioc 会怎么样做呢

public class Person {private Food food;public void eat() {System.out.println("I eat food:{}", food.toString());}
}复制代码

它会在你吃的时候将食物准备好,不需要你自己做饭。因为它认为:吃饭的人不应该身兼厨师的角色。
借用《spring 揭秘》中的漫画再说明一下吧(因为我不会画吃饭的漫画)。它的意思是:穿衣服出门。如果不使用Ioc,你就得自己去取衣服穿上。用了IOC,已经有美女给你拿过来并帮你穿上(有没有一种大款的感觉)。IOC就是让你当大款,你只需要发挥自己的特长挣钱就可以了,其它的让小秘来。

其实上面就是IOC的核心思想,也就是它要解决的问题:让你脱离对依赖对象的维护,只需要随用随取,不需要关心依赖对象的任何过程。(是不是感觉特别简单)

二、IOC的技术实现方式

接下来的问题是如何将依赖的对象准备好呢(依赖注入),常用的有两种方式:构造方法注入和setter注入(虽然大家都很熟悉了,但还请原谅我再说一下)
构造器注入,它就代表了当Person这个对象生成时,就准备好了:即无论你吃不吃饭,饭就在那里,不离不弃

public Person(Food food) {this.food = food;
}复制代码

setter注入,有所不同:俺不是那么随便的食物,你得喊我(set)俺才过来,有种闷骚的感觉。反正我就喜欢这种……

public void setFood(Food food) {this.food = food;
}复制代码

但无论前提哪一种注入方法,你总得有小秘来执行吧!!!so,你只需要默默地躺在那来享受,小秘带来百般绝技!!!

三、IOC容器

小秘绝技虽然精彩,但要实现却并不那么容易。它需要一系列技术要实现。首先它需要知道服务的对象是谁,以及需要为服务对象提供什么样的服务。提供的服务指:要完成对象的构建(即把饭做好),将其送到服务对象即完成对象的绑定(即把饭端到我面前)。
上面的话别看糊涂了,再声明一下,Ioc需要实现两个技术:

  • 对象的构建
  • 对象的绑定

对于这两个方面技术的实现具有很多的方式:硬编码(Ioc 框架都支持),配置文件(我们的重点),注解(最洁的方式)。但无论哪种方式都是在Ioc容器里面实现的(我们可以理解为一个大池子,里面躺着各种各样的对象,并能通过一定的方式将它们联系起来)
spring提供了两种类型的容器,一个是BeanFactory,一个是ApplicationContext(可以认为是BeanFactory的扩展),下面我们将介绍这两种容器如何实现对对象的管理。

3.1 BeanFactory

如果没有特殊指定,默认采用延
迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对 该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需 要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的 IoC容器选择。
我们先来看一下BeanFactory类的关系图(如下所示)

有三个很重要的部分:

  • BeanDefinition 实现Bean的定义(即对象的定义),且完成了对依赖的定义
  • BeanDefinitionRegistry ,将定义好的bean,注册到容器中(此时会生成一个注册码)
  • BeanFactory 是一个bean工厂类,从中可以取到任意定义过的bean
    最重要的部分就是BeanDefinition,它完成了Bean的生成过程。一般情况下我们都是通过配置文件(xml,properties)的方式对bean进行配置,每种文件都需要实现BeanDefinitionReader,因此是reader本身现了配置文字 到bean对象的转换过程。当然我们自己也可以实现任意格式的配置文件,只需要自己来实现reader即可。
    Bean的生成大致可以分为两个阶段:容器启动阶段和bean实例化阶段

    容器启动阶段:

  • 加载配置文件(通常是xml文件)
  • 通过reader生成beandefinition
  • beanDefinition注册到beanDefinitionRegistry

bean实例化阶段:
当某个bean 被 getBean()调用时
bean需要完成初时化,以及其依赖对象的初始化
如果bean本身有回调,还需要调用其相应的回调函数
从 上面我们也可以知道,beanDefinition(容器启动阶段)只完成bean的定义,并未完成初始化。初始是通过beanFactory的getBean()时才进行的。
Spring Ioc在初始化完成之后,给了我们提供一些方法,让我们来改变一些bean的定义
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer:使我们可能通过配置文件的形式,配置一些参数
PropertyOverrideConfigurer :则可以覆盖原本的bean参数
CustomEditorConfigurer :则提供类型转换支持(配置文件都是string,它需要知道转换成何种类型)
Bean的初始化过程:

如果你认为实例化的对象就是通过我们定义的类new 出来的,那就大错特错了,其实这里用到了AOP机制,生成了其代理对象(通过反射机制生成接口对象,或者是通过CGLIB生成子对象)
bean的具体装载过程是由beanWrapper实现的,它继承了PropertyAccessor (可以对属性进行访问)、PropertyEditorRegistry 和TypeConverter接口 (实现类型转换,就上前面说的)。
完成设置对象属性之后,则会检查是否实现了Aware类型的接口,如果实现了,则主动加载

BeanPostprocessor 可以帮助完成在初始化bean之前或之后 帮我们完成一些必要工作,比如我们在连接数据库之前将密码存放在一个加密文件,当我们连接数据库之前,需要将密码进行加载解密。只要实现 相应的接口即可

public interface BeanPostProcessor {/*** Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}* or a custom init-method). The bean will already be populated with property values.* The returned bean instance may be a wrapper around the original.* @param bean the new bean instance* @param beanName the name of the bean* @return the bean instance to use, either the original or a wrapped one; if* {@code null}, no subsequent BeanPostProcessors will be invoked* @throws org.springframework.beans.BeansException in case of errors* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet*/Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;/*** Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}* or a custom init-method). The bean will already be populated with property values.* The returned bean instance may be a wrapper around the original.* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean* instance and the objects created by the FactoryBean (as of Spring 2.0). The* post-processor can decide whether to apply to either the FactoryBean or created* objects or both through corresponding {@code bean instanceof FactoryBean} checks.* <p>This callback will also be invoked after a short-circuiting triggered by a* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,* in contrast to all other BeanPostProcessor callbacks.* @param bean the new bean instance* @param beanName the name of the bean* @return the bean instance to use, either the original or a wrapped one; if* {@code null}, no subsequent BeanPostProcessors will be invoked* @throws org.springframework.beans.BeansException in case of errors* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet* @see org.springframework.beans.factory.FactoryBean*/Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;}复制代码

在完成postProcessor之后,则会看对象是否定义了InitializingBean 接口,如果是,则会调用其afterProper- tiesSet()方法进一步调整对象实例的状态 ,这种方式并不常见。spring还提供了另外一种指定初始化的方式,即在bean定义中指定init-method 。
当这一切完成之后,还可以指定对象销毁 的一些回调,比如数据库的连接池的配置,则销毁前需要关闭连接等。相应的可以实现DisposableBean 接口或指定destroy-method

3.2 ApplicationContext

ApplicationContext 容器建立BeanFactory之上,拥有BeanFactory的所有功能,但在实现上会有所差别。我认为差别主要体现在两个方面:1.bean的生成方式;2.扩展了BeanFactory的功能,提供了更多企业级功能的支持。
1.bean的加载方式
BeanFactory提供BeanReader来从配置文件中读取bean配置。相应的ApplicationContext也提供几个读取配置文件的方式:

  • FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径
  • ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
  • WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
  • AnnotationConfigApplicationContext
  • ConfigurableWebApplicationContext
    另外一个比较重要的是,ApplicationContext采用的非懒加载方式。它会在启动阶段完成所有的初始化,并不会等到getBean()才执行。所以,相对于BeanFactory来 说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容 器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场景中, ApplicationContext类型的容器是比较合适的选择。

    ApplicationContext 还额外增加了三个历能:ApplicationEventPublisher,ResourceLoader,MessageResource

ResourceLoader

ResourceLoader并不能将其看成是Spring独有的功能,spring Ioc只是借助于ResourceLoader来实现资源加载。也提供了各种各样的资源加载方式:

  • DefaultResourceLoader 首先检查资源路径是否以classpath:前缀打头,如果是,则尝试构造ClassPathResource类 型资源并返回。否则, 尝试通过URL,根据资源路径来定位资源
  • FileSystemResourceLoader 它继承自Default-ResourceLoader,但覆写了getResourceByPath(String)方法,使之从文件系统加载资源并以 FileSystemResource类型返回
    • ResourcePatternResolver 批量查找的ResourceLoader

      spring与ResourceLoader之间的关系

      所有ApplicationContext的具体实现类都会直接或者间接地实现AbstractApplicationContext,AbstactApplicationContext 依赖了了DeffaultResourceLoader, ApplicationContext 继承了ResourcePatternResolver,所到头来ApplicationContext的具体实现类都会具有DefaultResourceLoader 和 PathMatchingResourcePatterResolver的功能。这也就是会什么ApplicationContext可以实现统一资源定位。

ApplicationEventPublisher(在介绍spring事件的时候再详细讲)

  1. ApplicationEvent:继承自EventObject,同时是spring的application中事件的父类,需要被自定义的事件继承。
  2. ApplicationListener:继承自EventListener,spring的application中的监听器必须实现的接口,需要被自定义的监听器实现其onApplicationEvent方法
  3. ApplicationEventPublisherAware:在spring的context中希望能发布事件的类必须实现的接口,该接口中定义了设置ApplicationEventPublisher的方法,由ApplicationContext调用并设置。在自己实现的ApplicationEventPublisherAware子类中,需要有ApplicationEventPublisher属性的定义。
  4. ApplicationEventPublisher:spring的事件发布者接口,定义了发布事件的接口方法publishEvent。因为ApplicationContext实现了该接口,因此spring的ApplicationContext实例具有发布事件的功能(publishEvent方法在AbstractApplicationContext中有实现)。在使用的时候,只需要把ApplicationEventPublisher的引用定义到ApplicationEventPublisherAware的实现中,spring容器会完成对ApplicationEventPublisher的注入。

MessageSource

提供国际化支持,不讲了,有需要请转至:blog.sina.com.cn/s/blog_85d7…

#四、最佳实践
注解扫描

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="org.spring21"/></beans>复制代码

component/service/controller注解


@Component
public class Person {@Resourceprivate Food food;public void setFood(Food food) {this.food = food;}
}复制代码

bean的前置后置

@Component
public class Person {@Resourceprivate Food food;public setFood(Food food) {this.food = food;}@PostConstructpublic void wash() {System.out.println("饭前洗手");}@PreDestroypublic void brush() {System.out.println("饭后刷牙");}
}复制代码

IoC-spring 的灵魂(带你轻松理解IOC思想及bean对象的生成过程)相关推荐

  1. 资深大牛带你了解源码!带你轻松理解Android-Hook机制,已开源

    前言 我今年38岁,失业前是南方二线城市某知名互联网公司的部门技术主管,婚姻幸福,膝下有一儿一女,组成一个好字,房子车子:有一辆十几万的汽车,一套月供八千的房子,妻子全职在家带娃,家里的一切开销全部指 ...

  2. Spring学习手札(三)理解IoC 拯救不开心

    Inverse of Control,翻译成"控制反转",是Spring的核心.IoC不是一种技术,而是一种设计思想.就是将原本在程序中手动创建对象的控制权(new Object( ...

  3. 手推公式带你轻松理解L1/L2正则化

    文章目录 前言 L1/L2正则化原理 从数学的角度理解L1/L2正则化 从几何的角度理解L1/L2正则化 L1/L2正则化使用情形 前言 L1/L2正则化的目的是为了解决过拟合,因此我们先要明白什么是 ...

  4. 【机器学习】带你轻松理解什么是强化学习中的状态动作函数 ?

    系列文章目录 第十八章 Python 机器学习入门之强化学习 目录 系列文章目录 前言 一.状态动作函数的定义 二.直观理解 三.将状态动作函数与回报和策略联系起来 总结 前言 强化学习中的状态动作函 ...

  5. 书单收藏 | 17本中信版畅销书从科普、商业到技术带你轻松理解区块链

    本文来源:中信出版集团,该内容旨在传递更多市场信息,不构成任何投资建议. 区块链被列为我国核心技术自主创新的重要突破口! 区块链技术的集成应用在新的技术变革和产业变革中起着重要作用,加快推动区块链技术 ...

  6. 带你轻松理解Zookeeper的选举机制

    一,Zookeeper选举过程中服务器的状态. LOOKING:寻找leader状态,该状态下,服务器认为当前集群没有leader,会发起leader选举.在选举过程中,所有服务器的状态都是LOOKI ...

  7. 带你轻松理解python类的一些基础用法(❁´◡`❁)

  8. ioc di php,PHP程序员如何理解IoC/DI

    思想 思想是解决问题的根本 思想必须转换成习惯 构建一套完整的思想体系是开发能力成熟的标志 --<简单之美>(前言) . "成功的软件项目就是那些提交产物达到或超出客户的预期的项 ...

  9. Spring学习-理解IOC和依赖注入

    最近刚买了一本介绍ssm框架的书,里面主要对Mybatis.spring.springmvc和redis做了很多的讲解,个人觉得虽然有的内容我看不懂,但是整体上还是不错的.最近正在学习中,一边学习一边 ...

最新文章

  1. Bootstrap方法为页面添加一个弹出框
  2. gitlab的安装和使用
  3. 微信公众平台开发 - 动手篇。使用weinxinFundation开始一个微信公众平台的开发
  4. markdown输出为pdf没有图片怎么办?
  5. codeforces B. High School: Become Human
  6. java源码导入eclipse_spring framework源码下载并导入eclipse
  7. 库克:苹果商店收取30%佣金是应该的
  8. asp脚本和php脚本,有经典ASP的缓存脚本吗?
  9. C#控件的闪烁问题解决方法总结
  10. 4.35V锂电充电IC
  11. ibm的odm使用_使用IBM ODM API生成规则工件
  12. linux 内核 禁止抢占,内核抢占实现(preempt)
  13. 计算机网络层之 P2P
  14. 90后薪资5年翻10倍,靠的不是钢琴手,而是真家伙!
  15. 蓝牙Controller框架梳理
  16. 华为在高端手机市场首尝被小米击败的滋味,这是一个信号
  17. 两台笔记本电脑共享屏幕(其中一台电脑当做另外一台电脑的扩展屏幕,多屏显示)
  18. 评论:后乔布斯时代 苹果不能再单枪匹马
  19. fgets()函数的详解-使用技巧-C语言基础
  20. 如何隐藏excel公式?如何取消隐藏?

热门文章

  1. php ci框架 模板输出,CodeIgniter模板引擎使用实例
  2. 判断有向图g中顶点i到顶点j是否有路径_号称图的最短路径算法--Floyd算法
  3. Python进阶之路:namedtuple
  4. tensorflow环境下的识别食物_研究室秒变后厨,TensorFlow被馋哭!日本团队用深度学习识别炸鸡,救急便当工厂...
  5. Vue中绑定值与字符串拼接以及结合三目表达式实现是否为空判定的使用
  6. Leaflet中使用Leaflet.fullscreen插件实现全屏效果
  7. Vue+Openlayer使用Draw实现交互式绘制多边形并获取面积
  8. C#中怎样获取System.Drawing.Color的所有颜色对象并存到数组中
  9. Python中使用python -m pip install --upgrade pip升级pip时老是不成功
  10. npm升级以及使用淘宝npm镜像