1. spring ioc的启动过程:
spring IOC 容器的启动步骤可总结如下:
1 初始化 ApplicationContext
环境属性的初始化和验证,启动时间记录和相关标记设置,应用事件和监听者的初始化。
2 准备好容器中的 BeanDefinition (eager-initializing beans)
对 BeanDefinition 的解析、扫描和注册,BeanDefinition 的扫描和注册大致可以分为 XML 和注解两种,两种方式各自使用的组件有所不同,该步骤的时间也可以在最前面。
3 初始化 BeanFactory
准备好 BeanFactory 以供 ApplicationContext 进行使用,对接下来将要使用到的 Bean 进行实例化,资源进行准备,属性进行设置。
4 注册 BeanPostProcessors
BeanPostProcessors 是进行扩展的关键组件,需要在该步骤中进行注册,可分为两种类型: 一种是框架使用者提供的,用于特定业务功能的,另一种是框架开发者提供的,用于扩展框架功能。
5 调用 BeanDefinitionRegistryPostProcessor
BeanDefinitionRegistryPostProcessor 是一种功能增强,可以在这个步骤添加新的 BeanDefinition 到 BeanFactory 中。
6 调用 BeanFactoryPostProcessor
BeanFactoryPostProcessor 是一种功能增强,可以在这个步骤对已经完成初始化的 BeanFactory 进行属性覆盖,或是修改已经注册到 BeanFactory 的 BeanDefinition。
7 初始化 MessageSource 和 ApplicationEventMulticaster
MessageSource 用于处理国际化资源,ApplicationEventMulticaster 是应用事件广播器,用于分发应用事件给监听者。
8 初始化其他 Bean 和进行其他的的上下文初始化
主要用于扩展
9 注册 ApplicationListene
将 ApplicationListene 注册到 BeanFactory 中,以便后续的事件分发
10 实例化剩余的 Bean 单例
步骤 4 到 9 都对一些特殊的 Bean 进行了实例化,这里需要对所有剩余的单例 Bean 进行实例化
11 启动完成
资源回收,分发"刷新完成"事件
2. spring获取和实例化bean的过程:
Spring是通过递归的方式获取目标bean及其所依赖的bean的;
Spring实例化一个bean的时候,是分两步进行的,首先实例化目标bean,然后为其注入属性。
结合这两点,也就是说,Spring在实例化一个bean的时候,是首先递归的实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性的。
3. bean实例化的过程
createBeanInstance 调用对象的构造方法 实例化对象
populateBean 填充属性 为多bean的依赖属性进行填充
initializeBean 调用spring的init方法 初始化
即 创建对象(分配空间)-> 属性填充(设置对象属性)-> 初始化
分析可知 对象的循环依赖主要会发生在第一步构造器循环依赖和setter循环依赖
构造器循环依赖问题:
this .singletonsCurrentlylnCreation.add(beanName)将当前正要创建的bean 记录在缓存中
Spring 容器将每一个正在创建的bean 标识符放在一个“当前创建bean 池”中, bean 标识
柏:在创建过程中将一直保持在这个池中,因此如果在创建bean 过程中发现自己已经在“当前
创建bean 池” 里时,将抛出BeanCurrentlylnCreationException 异常表示循环依赖;而对于创建完毕的bean 将从“ 当前创建bean 池”中清除掉。
setter循环依赖
解决方案: 三级缓存
singletonFactories : 进入实例化阶段的单例对象工厂的cache (三级缓存)
earlySingletonObjects :完成实例化但是尚未初始化的,提前暴光的单例对象的Cache (二级缓存)
singletonObjects:完成初始化的单例对象的cache(一级缓存)
一级缓存:用于存放完全初始化好的 bean **/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** 二级缓存:存放原始的 bean 对象(尚未填充属性),用于解决循环依赖 */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** 三级级缓存:存放 bean 工厂对象,用于解决循环依赖 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/**
bean 的获取过程:先从一级获取,失败再从二级、三级里面获取
创建中状态:是指对象已经 new 出来了但是所有的属性均为 null 等待被 init
*/
检测循环依赖的过程如下:
A 创建过程中需要 B,于是 A 将自己放到三级缓里面 ,去实例化 B
B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了!
然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
B 顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)
然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,然后完成创建,并将自己放到一级缓存里面
4. spring如何实现单例模式
使用单例注册表hashmap结构,若定义为单例模式 则检查注册表 根据beanName 检查为null时才创建bean 然后向注册表注册。
5. bean的创建过程
-
转换beanName
1.1 处理FactoryBean,比如beanName 为 &user,此时会将& 截取掉
1.2 根据beanName查找真正的Name。比如我们定义了一个bean,id=“user”,name=“student”,alias=“teacher”。那么我们在getBean(name)时,name 传user,student,teacher 任何一个都可以得到我们定义的bean,切转换后的beanName是 user。 -
尝试从缓存或者提前暴露的BeanFactory中获取实例
-
bean的实例化(存在FactoryBean的情况)
3.1 当前bean非FactoryBean,直接返回
3.2 当前bean为FactoryBean,则调用被覆盖的getObject()方法获取实例。 -
原型类型循环引用的检查(多例模式下的循环引用检查)
-
尝试从父类工厂中实例化bean
5.1 parentBeanFactory 不为空,且parentBeanFactory 包含要查找bean的定义。即通过beanName,能找到BeanDefinition
5.2 满足条件1,则尝试从parentBeanFactory中加载bean -
转换RootBeanDefinition,并合并父类属性
6.1 从缓存(容器初始化时解析配置文件得到并存入缓存)中获取GenericBeanDefinition,并将其转换为RootBeanDefinition。spring源码系列-容器之XmlBeanFactory
6.2 如果parent不为空,则需要将parent的属性也合并进来。 -
查找并实例化依赖-depend on
7.1 查找依赖-dependOn(这里的依赖,指的不是我们常说的依赖注入属性。而是当前bean可能用到特定的bean,而这个特定的bean 要放在当前bean之前加载。)
7.2 初始化依赖-dependOn -
根据不同的scope做bean的创建
8.1. 尝试从缓存中加载
8.2. 如果缓存不存在,则将bean标记为正在创建
8.3. 调用ObjectFactory.getObject()方法创建bean
8.4. 移除正在创建状态
8.5. 删除各种创建时状态,并添加缓存
8.3.1ObjectFactory.getObject()大致包含如下流程:
a1. 处理MethodOverrides
b2. 实例化的前置处理
c3. 根据不同的策略实例化bean,并包装成beanWapper
d4. 提前暴露beanFactory
e5. 属性填充
f6. 调用初始化方法,即各种处理器
g7. 注册销毁相关方法
- 类型转换
到这里bean的创建基本上就结束来,但是有时候会存在,得到的类型和我们要求的类型requireType不一致的情况,这个时候就需要进行类型的转换。
6. aop的实现方式
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
7. spring 实现aop
如果目标类实现了InvocationHandler接口,直接使用jdk动态代理。否则使用CGLIB
主要执行时的invoke步骤:
- .以下的几个判断,主要是为了判断method是否为equals、hashCode等Object的方法
- 获取当前bean被拦截方法链表chain
- 如果为空,则直接调用target的method
- 不为空,则逐一调用chain中的每一个拦截方法的proceed,这里的一系列执行的原因以及proceed执行的内容,我 在这里就不详细讲了,大家感兴趣可以自己去研读哈
8. spring aop 应用场景
权限、缓存、日志。