SpringIOC
IOC
IOC原理
IOC(Inversion of Control):控制反转
- Spring Core最核心的部分
- 需要先了解依赖注入(Dependency Inversion)
依赖注入含义
把底层类作为参数传递给上层类,实现上层对下层的“控制”
使用依赖注入的代码实例
需要修改轮胎时只用改一下轮胎就行了,不再需要大动干戈
IOC、DI、DL的关系
DL(Dependency Lookup):依赖查找(已经被抛弃,需要用户自己使用API查找对象)
依赖注入的方式
- Setter
- 实现特定属性的pulic setter来让IOC容器调用注入所依赖类型的对象
- Interface
- 实现特定的接口以供IOC容器注入所依赖类型的对象
- Constructor
- 基于构造函数,实现特定参数的构造函数,在创建对象时让IOC容器注入所依赖类型的对象
- Annotation
- 基于注解,通过Java的注解机制来让IOC容器注入所依赖类型的对象
- 如
Autowired
依赖倒置原则、IOC、DI、IOC容器的关系
- 依赖倒置原则是一种思想:高层模块不应该依赖低层模块,两者都应该依赖其抽象
- 前图那一大堆new就是IOC容器可以帮我们做的事
IOC容器的优势
- 避免在各处使用new来创建类,并且可以做到维护统一
- 创建示例时不需要了解其中的细节
- 没有使用IOC容器时候需要自己一个一个new,需要理清整个流程
- 使用IOC容器后在进行这个工作是反过来的,从最上层往下查找依赖关系,再从最下层往上一步一步去new,IOC容器可以隐藏具体创建实例的细节,蓝色部分即为被隐藏部分。
Spring的IOC容器
- Spring启动时读取应用程序的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表
- 根据这张注册表去实例化Bean
- 装配好Bean之间的依赖关系,为上层提供准备就绪的运行环境
- Spring提供一个配置文件描述Bean,Bean之间的依赖关系
- 利用Java语言的反射功能实例化Bean,并建立Bean之间的依赖关系。
Spring IOC支持的功能
- 依赖注入
- 依赖检查
- 自动装配
- 支持集合
- 指定初始化方法和销毁方法
- 支持回调方法
- 需要实现Spring接口
- 应谨慎使用
Spring IOC容器的核心接口
- BeanFactory
- ApplicationContext
Spring中的核心接口和类
BeanDefinition
主要用来描述Bean的定义
如上图,Spring启动时会将XML或注解中Bean的定义解析成Spring内部的
BeanDefinition
BeanDefinitionRegistry
- 提供向IOC容器注册
BeanDefinition
对象的方法
BeanFactory:Spring框架最核心的接口
提供IOC的配置机制
包含Bean的各种定义,便于实例化Bean
在实例化时建立Bean之间的依赖关系,将Bean自身从Bean客户端的配置中解放出来
Bean生命周期控制
BeanFactory体系结构(了解即可):
- ListableBeanFacotry:定义了访问容器中Bean基本信息的若干方法,如查看Bean的个数,获取某类型Bean的配置名,查看容器中是否包括某一Bean等方法。
- HierachicalBeanFacorty:父子级联IOC容器的接口,可以通过接口方法访问父容器。通过此接口,Spring可以建立父子层级的容器关联体系,子容器可以访问父容器中的Bean,但是父容器不能访问子容器。Spring使用父子容器实现了很多功能,如在Spring MVC中,展现层的Bean位于一个子容器中,而业务层和持久层的Bean位于父容器中,这样展现层的Bean就可以用业务层和持久层的Bean,而业务层和持久层就看不到展现层的Bean。
- ConfigurableBeanFactory:增强了IOC容器的可定制性,定义了设置类装载器,属性编辑器以及属性初始化后置处理器等方法
- AutowireCapableBeanFactory:定义了将容器中的Bean按某种规则进行自动装配(如名字等)
- SingletonBeanRigistry:允许在运行期间向容器注册Singleton实例Bean的方法
BeanFactory源码解析:
- 各种
getBean()
方法,以供按照不同规则查找Bean,如名字或类别 isSingleton(String var1)
方法,判断Bean是否单例存在, 默认情况下Bean都是单例存在
- 各种
BeanFactory与ApplicationContenxt比较
由于BeanFactory功能还不够强大,Spring在Beanfactory基础上还设计了一个更为高级的接口,即ApplicationContenxt,其是BeanFactory的子接口之一
- BeanFactory是Spring框架的基础设施,面向Spring
- ApplicationContenxt面向使用Spring框架的开发者
ApplicationContenxt的功能(继承了多个接口)
- 继承了BeanFactory接口,能够管理、装配Bean
- 继承了ResourcePatternResolver,能够加载资源文件
- 继承了MessageSource,能够实现国际化等功能
- 继承了ApplicationEventPublisher,能够发布事件、注册监听器,实现监听机制
- SpringBoot中
run()
方法一路查看源码,最后会发现创建的就是上图最下面的容器
Spring IOC代码实例
通过ApplicationContext获取Bean
显式装配Bean
首先写一个配置类配置Bean实例规则,给Person的Bean设置名字和Id
run()
方法的返回值是ApplicationContext的子类,可以用ApplicationContext来接收并在其中查找Bean。
SpringBoot方式装配Bean
SpringBoot允许扫描装配Bean到IOC容器中,其注解是@Component
和@ComponentScan
在实体类中使用@Component
标明此类要被扫描进入Spring IOC容器中
@ComponentScan
用来去扫描这些已经标记好的Bean,@SpringBootApplication
已经自带了@ComponentScan
所以不用我们再写。
@ComponentScan
默认只会扫描当前package和其子package。如果要扫描别的package需要指定一下扫描路径。
如何实现依赖注入
在需要注入的成员变量上标注@Autowired
即可
@Autowired
会根据属性的类型找到对应的Bean进行注入,其注入的机制默认是根据类型。
如果同时有两个同类型的Bean则会报错,可以用名字注入或者在某个Bean上加@Primary
,在多个Bean可选时优先选择它。
Spring IOC refreshContext源码
首先进行prepareRefresh()
:
- 设置容器的启动时间
- 撤销关闭状态,开启活跃状态
- 验证环境信息中必须存在的属性
obtainFreshBeanFactory()
用来获取BeanFactory实例
prepareBeanFactory(beanFactory)
对BeanFactory进行相关的设置,包括classLoader,表达式解析器等
postProcessBeanFactory(beanFactory)
在BeanFactory设置之后再进行一些后续操作,不同的容器操作不同,故此方法是空的,方便不同的容器去改写此方法
invokeBeanFactoryPostProcessors(beanFactory)
比较重要,逻辑比较复杂,主要是调用工厂后处理器处理各类Bean标签,扫描Bean文件并解析成一个个Bean。
registerBeanPostProcessors(beanFactory)
从Spring容器中找出实现BeanPostProcessor接口的Bean,并设置到BeanFactory的属性中,之后Bean被实例化时会调用BeanPostProcessor即Bean的后置处理器。(和AOP有相关)
initMessageSource()
初始化国际化相关方法
initApplicationEventMulticaster()
初始化事件广播器,用于事件发布
onRefresh()
方法体为空,是一个模版方法,不同的Spring容器会重写此方法做不同的事情,如Web程序的容器会在此创建内置Servlet容器(如tomcat)
registerListeners()
注册事件监听器
finishBeanFactoryInitialization(beanFactory)
实例化BeanFactory中已经被注册但是未被实例化的所有Bean(懒加载除外)
finishRefresh()
最后一个步骤,初始化生命周期处理器等事情。
Refresh方法总结:
- 为IOC容器以及Bean的生命周期管理提供条件
- 刷新Spring上下文信息,定义Spring上下文加载流程
Spring IOC getBean()方法
getBean()
用来加载Bean,在BeanFactory下有很多getBean()
方法,其实现主要在AbstractBeanFactory
首先获取BeanName
根据BeanName调用getSingleton方法获取Bean实例(也可能是工厂实例,获取实例后调用getObjectForBeanInstance方法试着从缓存或实例工厂中获取实例
如果从getSingleton方法中获取不到就尝试从ParentBeanFactory中去获取
。。。
然后会将对类的定义(Defination)如子类父类的定义等进行合并,将父类等属性合并到子类中去
如果存在依赖,递归实例化依赖
判断这个Bean是否是Singleto,如果是就返回单例,否则直接创建
最后做类型检查,创建的Bean是否是需要的Bean,完成后返回。
getBean()方法代码逻辑
- 转换BeanName
- 从缓存中加载实例
- 实例化Bean
- 检查
ParentBeanFactory
- 初始化依赖的Bean
- 创建Bean
相关面试题
Spring Bean作用域(scope)
- singleton:Spring的默认作用域,容器里拥有唯一的Bean实例
- prototype:针对每个
getBean()
请求,容器都会创建一个Bean实例- 三思,频繁创建和销毁Bean开销较大
如果是web容器,则还支持三种作用域
- request:会为每个http请求创建一个Bean实例
- session:会为每个sesison创建一个Bean实例
- globalSession:会为每个全局http session创建一个Bean实例,该作用域仅对Portlet有效
- globalSession是Portlet规范提出的概念
- 该sesison对所有构成某个Portlet web应用的各种不同Portlet所共享。
- 故仅在使用Portlet时有效
Spring Bean的生命周期
- Bean 容器找到配置文件中 Spring Bean 的定义。
- Bean 容器利用 Java Reflection API 创建一个Bean的实例。
- 可能有循环依赖的问题(一级缓存是单例缓存池(singletonObjects),二级缓存是早期对象(earlySingletonObjects),三级缓存是一个包裹对象ObjectFactory(registeredSingletons),通过getObject获取到早期对象。)
- 两级可以解决循环依赖,三级为了解决切面的问题
- 但是IOC无法解决两种循环依赖,一种是非单例对象的,因为非单例对象不会放入缓存的。每次都是需要创建。二是通过构造器注入,也无法解决。从上面的流程可以看出,调用构造器创建实例是在createBeanInstance方法,而解决循环依赖是在populateBean这个方法中,执行顺序也决定了无法解决该种循环依赖
- 如果涉及到一些属性值 利用
set()
方法设置一些属性值。 - 如果发现实现了
*.Aware
接口,就调用相应的方法。- 如果 Bean 实现了
BeanNameAware
接口,调用setBeanName()
方法,传入Bean的名字。 - 如果 Bean 实现了
BeanClassLoaderAware
接口,调用setBeanClassLoader()
方法,传入ClassLoader
对象的实例。 BeanFactoryAware
- 如果 Bean 实现了
- 如果有和加载这个 Bean 的 Spring 容器相关的
BeanPostProcessor
对象,执行postProcessBeforeInitialization()
方法 - 如果Bean实现了
InitializingBean
接口,执行afterPropertiesSet()
方法。 - 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
- 如果有和加载这个 Bean的 Spring 容器相关的
BeanPostProcessor
对象,执行postProcessAfterInitialization()
方法 - 当要销毁 Bean 的时候,如果 Bean 实现了
DisposableBean
接口,执行destroy()
方法。 - 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
AOP创建Proxy对象的地方有两个:
- 常规一点的就是在BeanFactory.initializeBean()方法里面调用初始化方法之后,调用applyBeanPostProcessorsAfterInitialization() –> AbstractAutoProxyCreator.wrapIfNecessary()生成proxy对象。
- 还有就是在存在循环依赖的时候,在doCreateBean() –> addSingletonFactory(beanName, () –> getEarlyBeanReference(beanName, mbd, bean)); –> 然后在getSingleton()的时候从三级缓存中取对象的时候,会调用getEarlyBeanReference() –> AbstractAutoProxyCreator.wrapIfNecessary()生成proxy对象。
循环依赖 + AOP
1 | Copyprotected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { |
1处,创建bean对象,此时,属性什么的全是null,可以理解为,只是new了,field还没设置
2处,添加到第三级缓存;加进去的,只是个factory,只有循环依赖的时候,才会发挥作用
3处,把原始bean,存到exposedObject
4处,填充属性;循环依赖情况下,A/B循环依赖。假设当前为A,那么此时填充A的属性的时候,会去:
new B;
填充B的field,发现field里有一个是A类型,然后就去getBean(“A”),然后走到第三级缓存,拿到了A的ObjectFactory,然后调用ObjectFactory,然后调用AOP的后置处理器类:getEarlyBeanReference,拿到代理后的bean(假设此处切面满足,要创建代理);
经过上面的步骤后,B里面,field已经填充ok,其中,且填充的field是代理后的A,这里命名为proxy A。
B 继续其他的后续处理。
B处理完成后,被填充到当前的origin A(原始A)的field中
5处,对A进行后置处理,此时调用aop后置处理器的,postProcessAfterInitialization;前面我们说了,此时不会再去调用wrapIfNecessary,所以这里直接返回原始A,即 origin A
6处,去缓存里获取A,拿到的A,是proxy A
7处,我们梳理下:
exposedObject:origin A
bean:原始A
earlySingletonReference: proxy A
此时,下面这个条件是满足的,所以,exposedObject,最终被替换为proxy A:
1
2
3Copyif (exposedObject == bean) {
exposedObject = earlySingletonReference;
}