Spring源码之Bean的加载(一)
bean 的加载
之前文章主要分析了对 XML 配置文件的解析,接下来就是对 bean 的加载进行分析,同样开始用最开始的代码为入口。
入口代码 getBean
public void testSimpleLoad(){
final BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
final MyTestBean myTestBean = (MyTestBean) beanFactory.getBean("myTestBean");
assertEquals("testStr",myTestBean.getTestStr());
}
从这里我们快速先大致了解一下是如何实现的。
从 BeanFactory 接口中我们选择对应实现类为 AbstractBeanFactory。
Object getBean(String name) throws BeansException;
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//通过三种形式获取beanName, 一个是原始的beanName,一个是加了&的,一个是别名
//返回容器中真是的beanName
final String beanName = transformedBeanName(name);
Object bean;
/**
* 检查缓存中或实例工厂中是否存在对应实例
* 因为在创建单例bean的时候会存在依赖注入的情况,Spring为了避免循环依赖,创建bean的原则是不等bean创建完成就会创建bean的ObjectFactory提早曝光
* 也就是将ObjectFactory放入到了缓存中,一旦下个bean创建时候需要依赖上一个bean则直接使用ObjectFactory
*/
// Eagerly check singleton cache for manually registered singletons.
//尝试从缓存中获取或者从singleFactories中的ObjectFactory中获取
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
//如果Bean还在创建中,则说明是循环引用
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//如果是普通bean,直接返回,如果是FactoryBean,则返回它的getObject
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else {
//只有在单例情况下Spring才会尝试解决循环依赖,在原型模式下如果存在A->B->A的话就会抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//检查工厂中是否存在bean定义
BeanFactory parentBeanFactory = getParentBeanFactory();
//如果beanDefinitionMap中不包括当前beanName则会尝试从parentBeanFactory中检测
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//主要针对FactoryBean,将Bean的&重新加上
//将转换过后的BeanName恢复回原先的样子
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
//递归到BeanFactory中寻找
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
//如果有参数,则委派父类容器根据指定名称和参数查找
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
//委派父级容器根据指定名称和类型查找
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
//委派父级容器根据指定名称查找
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
//如果不是仅仅做类型检查则是创建bean,进行记录
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}try {
//将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,如果指定的BeanName是子bean的话还会合并父类的相同属性
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//对合并的BeanDefinition做验证,主要看属性是否为abstract的
checkMergedBeanDefinition(mbd, beanName, args);
String[] dependsOn = mbd.getDependsOn();
//如果存在依赖则需要递归实例化依赖的bean
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
//缓存依赖调用
registerDependentBean(dep, beanName);
try {
//递归调用getBean方法,注册Bean之间的依赖(如C需要晚于B初始化,而B需要晚于A初始化)
//初始化依赖的bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// 实例化依赖的bean后就可以实例化mbd本身了
// 如果BeanDefinition为单例
if (mbd.isSingleton()) {
//创建Bean实例对象,并且注册给所依赖的对象
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
//从单例缓存中删除bean实例
//因为单例模式下为了解决循环依赖,可能它已经存在了,所以将其销毁
destroySingleton(beanName);
throw ex;
}
});
//如果是普通bean,直接返回,如果是FactoryBean,则返回它的getObject
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 如果BeanDefinition为prototype
else if (mbd.isPrototype()) {
// 每次创建一个新的对象
Object prototypeInstance = null;
try {
//注册当前创建的prototype对象为正在创建中
beforePrototypeCreation(beanName);
//创建原型对象实例
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
//将先前注册的正在创建中的Bean信息给抹除掉
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
//如果不是原型模式和单例模式则可能是: request、session、application等生命周期
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
//Bean定义资源中没有配置生命周期范围,则Bean定义不合法
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
//如果bean的scope不是singleton和prototype,则调用scope.get()来选择合适的加载策略
Object scopedInstance = scope.get(beanName, () -> {
//注册当前创建的prototype对象为正在创建中
beforePrototypeCreation(beanName);
try {
//创建bean
return createBean(beanName, mbd, args);
}
finally {
//将先前注册的正在创建中的Bean信息给抹除掉
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread;
consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}// Check if required type matches the type of the actual bean instance.
//检查需要的类型是否符合bean的实际类型
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
//返回bean实例
return (T) bean;
}
可以看到该方法代码量很多,我们先大概整理一下流程:
1.转换对应 beanName 转换对应 beanName 的意思就是,这里我们传入的 beanName 可能是别名也可能是 FactoryBean,所以要进行解析。
包括:
- 去除 FactoryBean 的修饰符,也就是如果 name=&jack,那么会去除&而使 name=jack
- 取指定 alias 所表示的最终 beanName,如果别名 A 指向名称 B 的 bean,则返回 B 的 bean;如果别名 A 指向别名 B,别名 B 又指向别名 C 则返回 C 的 bean
因为在创建单例bean的时候可能会存在依赖注入的情况,Spring在创建依赖的时候为了避免循环依赖,创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提前曝光加入到缓存中,一旦下一个bean创建时候需要依赖上一个bean则直接使用ObjectFactory
。3.bean 的实例化 如果从缓存中得到的是未实例化的 bean,则需要进行实例化操作,需要注意:缓存中记录的是最原始的 bean 状态,不一定是我们想要 bean。我们需要的是工厂 bean 中定义的 factory-method 方法中返回的 bean,而
getObjectForBeanInstance
方法就是完成这个工作的。4.原型模式的依赖检查 只有单例的情况下才会尝试解决循环依赖,如果存在 A 中有 B 属性,B 中有 A 属性,当依赖注入时候就会产生 A 还没创建完的时候因为对于 B 的创建再次返回创建 A,造成循环依赖。也就是
isPrototypeCurrentlyInCreation(beanName)
为 true。5.检测 parentBeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
如果从缓存中没有获取到,就从父类工厂中去加载。
下一行去进行判断,如果 parentBeanFactory 为空一切都是浮云,随后去检测如果当前加载的 XML 配置中不包含 beanName 所对应的配置,就只能到 parentBeanFactory 中尝试,然后再去递归调用 getBean 方法。
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
6.将存储 XML 配置文件的 GenericBeanDefinition 转换为 RootBeanDefinition 之前说过从 XML 中读取到的 bean 信息是存在 GenericBeanDefinition 中的,但是后续的 bean 处理都是针对 RootBeanDefinition 的,所以要进行一个转换,同时判断父类 bean 不为空的话,一并合并父类属性。
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
7.寻找依赖 在 bean 初始化中可能会用到某些属性,而某些属性可能是动态配置的,并且依赖其他 bean,这时候需要先加载所依赖的 bean。
8. 针对不同 scope 进行 bean 的创建 该阶段对不同的 scope 进行 bean 的创建,默认的是 singleton,还有 prototype、request 等。
9.类型转换 到这里基本上 bean 的创建就已经结束了,这一步对 bean 进行一个转换,判断
requiredType != null
的话就转换为实际的类型。经过这些步骤后 bean 的创建就完成了,随后返回我们需要的 bean。总结
- 通过
getSingleton(beanName)
方法检查缓存中是否存在已经加载的 bean - 如果条件成立
if (sharedInstance != null && args == null)
,调用getObjectForBeanInstance(sharedInstance, name, beanName, null)
方法获取到 bean 实例,但有时存在比如 BeanFactory 的情况并不是直接返回实例本身而是返回指定方法的实例,并跳转到第 6 步 if (parentBeanFactory != null && !containsBeanDefinition(beanName))
如果当前不存在 beanName,调用方法 parentBeanFactory.getBean 去父类工厂中去找,父类工厂通常为空getMergedLocalBeanDefinition(beanName)
将存储 XML 配置文件的 GenericBeanDefinition 转换为 RootBeanDefinition,如果指定 beanName 是子 Bean 的话同时合并父类相关属性 Textif (mbd.isSingleton()) else if (mbd.isPrototype())
根据属性 bean 的属性 scope 进行不同的实例化if (requiredType != null && !requiredType.isInstance(bean))
通过指定需求类型不为空进行类型转换,否则进行强制转换
FactoryBean 的使用
Spring 通过反射机制来利用 bean 的 class 属性指定实现类来实例化 bean。在某些情况下,实例 bean 比较复杂,如果在
标签中需要大量的配置信息并且灵活性受限。Spring 中提供了
org.springframework.beans.factory.FactoryBean
的工厂接口,我们可以实现这个接口来定制实例化 bean 的逻辑。public interface FactoryBean {String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* 返回由FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例会放入到Spring容器的单例缓存池中
*/
@Nullable
T getObject() throws Exception;
/**
* 返回FactoryBean创建的bean类型
*/
@Nullable
Class> getObjectType();
/**
* 返回FactoryBean创建的bean实例的作用域是singleton还是prototype
*/
default boolean isSingleton() {
return true;
}}
当配置文件中
的 class 属性配置的实现类为 FactoryBean 时,通过getBean()方法获取返回到的不是 FactoryBean 本身,而是 FactoryBean#getObject()方法所返回的对象,相当于是代理了 getBean()方法。如果使用传统方式配置下面 Car 的
时,每一个属性则分别对应一个
标签。/**
* @author 神秘杰克
* 公众号: Java菜鸟程序员
* @date 2022/5/30
*/
public class Car {private int maxSpeed;
private String brand;
private double price;
// 省略 get / set
}
如果用 FactoryBean 的方式的话则会更加灵活一点,比如通过逗号分割符的方式一次性为 Car 所有属性赋值。
/**
* @author 神秘杰克
* 公众号: Java菜鸟程序员
* @date 2022/5/30
*/
public class CarFactoryBean implements FactoryBean {private String carInfo;
@Override
public Car getObject() throws Exception {
Car car = new Car();
final String[] infos = carInfo.split(",");
car.setBrand(infos[0]);
car.setMaxSpeed(Integer.parseInt(infos[1]));
car.setPrice(Double.parseDouble(infos[2]));
return car;
}@Override
public Class> getObjectType() {
return Car.class;
}public String getCarInfo() {
return carInfo;
}public void setCarInfo(String carInfo) {
this.carInfo = carInfo;
}@Override
public boolean isSingleton() {
return false;
}}
有了 CarFactoryBean 后,就可以在配置文件中使用下面这种自定义配置方式配置 Car Bean 了:
当调用
getBean("car")
时,通过反射机制发现了 CarFactoryBean 实现了 FactoryBean 接口,这时候 Spring 容器就调用接口方法 CarFactoryBean#getObject()方法返回。如果要获取 CarFactoryBean 的实例,则需要在 beanName 前面使用“&”作为前缀,比如:getBean("&car")
。缓存中获取单例 bean
了解 FactoryBean 的用法后,我们就可以了解 bean 加载的过程了。
我们知道单例在 Spring 的同一个容器内只会被创建一次,后续再获取 bean 直接从单例缓存中获取,注意这里也只是尝试加载。
首先尝试从缓存中加载,然后再次尝试从 singletonFactories 中加载。因为在创建单例 bean 时会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring 创建 bean 的原则就是不等 bean 创建完成就会将 bean 的 ObjectFactory 提前曝光加入到缓存中,一旦下一个 bean 创建时需要依赖上个 bean,则直接使用 ObjectFactory。
public Object getSingleton(String beanName) {
//参数true表示允许早期依赖
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//检查缓存中是否存在实例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//为空则加锁
synchronized (this.singletonObjects) {
//如果此时bean正在加载则不处理
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//当某些方法需要提前初始化时候则调用addSingleTonFactory方法讲对应的ObjectFactory初始化策略存入singletonFactories
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//调用预先设定的getObject方法
singletonObject = singletonFactory.getObject();
//记录到缓存中,注意:earlySingletonObjects和singletonFactories是互斥的
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
这个方法首先尝试从 singletonObjects 中获取实例,如果获取不到就从 earlySingletonObjects 中获取,如果还是获取不到则从 singletonFactories 里面获取 beanName 对应的 ObjectFactory,然后调用 ObjectFactory 的 getObject 来创建 bean,并放到 earlySingletonObjects 中,并且从 singletonFactories 中 remove 掉这个 ObjectFactory,后续所有内存操作都只为了循环依赖检测时候使用,也就是在 allowEarlyReference 为 true 的情况下使用。
总结
【Spring源码之Bean的加载(一)】这里面提到用来存储 bean 的不同的 map:
singletonObjects
:一级缓存,保存 BeanName 和创建 bean 实例之间的关系,bean name --> bean instanceearlySingletonObjects
:二级缓存,保存 BeanName 和创建 bean 实例之间的关系,与 singletonObjects 不同的是,当一个单例 bean 放入这里后,那么当 bean 还在创建过程中时,就可以通过 getBean 方法获取到了,目的是用来检测循环引用singletonFactories
:三级缓存,保存 BeanName 和创建 bean 的工厂之间的关系,bean name --> ObjectFactoryregisteredSingletons
:该实例是一个 Set 对象,用来保存当前所有已经注册的 bean
推荐阅读
- Seata源码分析——SessionManager
- 小游戏 横版2D射击小游戏 效果展示+完整项目源码超级简单,自己也可以做游戏玩了
- shell与Linux命令有什么区别(它们之间有联系吗?)
- NFS共享目录
- 如何在wp-admin之外管理wordpress插件()
- Python网络编程之socket与socketserver
- Spring|Spring IOC容器Bean管理XML注入集合类型属性
- linux之cp强制复制文件
- Gitlab 打tag标签
- 虚拟机快照功能介绍