Spring在项目中是经常用到,前一整子有个面试被问到Spring是怎么加载bean的,我当时答的是通过Java反射,而且毫不犹豫的就回答了。不过面试官却持怀疑态度,搞得我有点蒙蔽,回头也是看了很多资料。初步读了下SpringIOC的源码,用了大量的模板模式,好尼玛饶人,自己也不是看的特别懂,但是基本能理解IOC容器的原理及思想了。记录下吧。【PS:mod 20160809(情人节),最近在研究Spring源码,对该文章做修改,作为IOC的综述篇】
-
IOC(控制反转)与DI (依赖注入)
-
IOC(控制反转)
IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。
Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。 -
DI(依赖注入)
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。
IoC和DI的关系,其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“ 依赖注入” 明确描述了“被注入对象依赖IoC 容器配置依赖对象”。
-
-
Spring Bean的加载过程
- 容器的初始化做了几件事情:
- 资源的定位,就是定位你配置的xml文件
- 对resource文件进行解析,解析成spring 定义的BeanDefinition
- 对BeanDefinition 进行注册,其实就相当于把BeanDefinition 放到一个HashMap
- 依赖的注入:
- 根据BeanDefinition创建Bean实例
- 为Bean注入依赖的实例
下面的图很直观的看到:(from:http://blog.csdn.net/cutesource/article/details/6132650 )
这里补充一点,也就是文章开始那个面试题,准确的应该说Spring实例化bean的方式有多种,读下源码可以发现:[mod:20160813] 1.工厂方法创建Bean,2.构造器方法创建参数,3.有lookup,replace-method的bean采用cglib字节码,其余用Java反射。
-
Spring的继承体系【20160809】
说明:基于Spring3.0.5的版本。
这个接口体系是以BeanFactory和ApplicationContext为核心的。BeanFactory是IOC容器的最基本的接口,ApplicationContext一方面继承了BeanFactory体系的:ListableBeanFactory和HierachicalBeanFactory,具备了BeanFactory IOC容器的基本功能,另一方面,又通过继承MessageSource、ResourcePatternResource、EnvironmentCapable、ApplicationEventPublisher这些接口,拥有了更高级的IOC容器特性。
-
项目中Spring的过程
首先Web项目使用Spring是通过在web.xml里面配置org.springframework.web.context.ContextLoaderListener初始化IOC容器的,
<!-- 在启动时,实例化Spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
ContextLoaderListener继承了ContextLoader,并且实现ServletContextListener接口。当Server容器(一般指tomcat)启动时,会收到事件初始化。initWebApplicationContext方法是在org.springframework.web.context.ContextLoader类里面,它做了几件事情:首先是判断servletContext中是否已经注册了WebApplicationContext,如果有则抛出异常,避免重复注册。然后就是启用log,启动计时。接着进行了一系列复杂的东西, 将ServletContext设置成XmlWebApplicationContext的属性,这样就可以通过XmlWebApplicationContext读取web.xml中配置的spring文件,然后就是进行上面的IOC容器的实例化及bean的注入。
-
源码阅读技巧【20160809】
上面对于Spring的几个图和简单说明还是可以看的,不过Spring的源码读起来不是那么好看,这里给大家一个建议:沿主线把代码跟一遍,再回头去看辅助代码。因为Spring是一个很成熟的框架了,它在编码过程中要考虑的问题很多,比如IOC容器启动,我们只需要知道它是如何定位Resource,如何解析BeanDefinition,如何注册。那么在这个过程中,会有很多的基于安全、性能、扩展等等的考虑,加上Spring运用了很多的设计模式,也就产生了很多的我们眼中的冗余、不知道干啥的代码。所以,前几遍读源码,要能抛开这些代码跟着主线代码一直看下去,等你把这个过程搞清楚了,再回头去看一些前面的辅助代码。