前言

上一篇文章,跟大家一起阅读了一下Spring中getBean方法前面的流程,今天这篇文章,接着跟大家一起阅读它后面的流程。

getBean方法最终会调用doGetBean方法,其中有三个创建普通bean实例的关键方法。

1 单例bean

在Spring中,使用最多的可能是单例bean,即在Spring容器中,该bean从始至终都只有一个实例。

如果没有对bean的scope做特别的指定,Spring默认的scope就是单例的。是通过isSingleton方法判断的:image这个小技巧可以在工作中使用一下。

有时候默认值,可以不用设置,通过isSingleton方法的 || SCOPE_DEFAULT.equals(this.scope)可以实现默认值的情况,避免有很多地方需要设置默认值的情况。

单例bean是通过下面的方法实例化的:image它的源码逻辑比较清晰。

其中getSingleton是获取单例bean实例的主要方法,使用lamda表达式调用了createBean方法创建bean,最后通过getObjectForBeanInstance方法,处理FactoryBean的情况,上一篇文章专门介绍过的。

publicObjectgetSingleton(StringbeanName,ObjectFactory<?>singletonFactory){
//判断beanName如果为空则抛异常
Assert.notNull(beanName,"Beannamemustnotbenull");
//加锁
synchronized(this.singletonObjects){
//从一级缓存中获取bean实例
ObjectsingletonObject=this.singletonObjects.get(beanName);
//如果bean实例为空
if(singletonObject==null){
if(this.singletonsCurrentlyInDestruction){
thrownewBeanCreationNotAllowedException(beanName,
"Singletonbeancreationnotallowedwhilesingletonsofthisfactoryareindestruction"+
"(DonotrequestabeanfromaBeanFactoryinadestroymethodimplementation!)");
}
//如果beanName在正在创建的集合中,则抛异常
beforeSingletonCreation(beanName);
booleannewSingleton=false;
...
try{
//回调外面的方法
singletonObject=singletonFactory.getObject();
newSingleton=true;
}
catch(IllegalStateExceptionex){
//如果创建对象出现异常,则从一级缓存中获取bean实例
singletonObject=this.singletonObjects.get(beanName);
//如果bean实例为空,则抛异常
if(singletonObject==null){
throwex;
}
}
catch(BeanCreationExceptionex){
throwex;
}
finally{
//将beanName从正在创建的集合中删除
afterSingletonCreation(beanName);
}
if(newSingleton){
//将bean实例放入一级缓存
addSingleton(beanName,singletonObject);
}
}
//返回bean实例
returnsingletonObject;
}
}

下面用一张图总结一下,上面这段代码中的关键流程:image

其中createBean方法是创建bean的核心方法,逻辑非常多,下一篇文章会有专门介绍,这里就不过多介绍了。

2 多例bean

如果定义bean的时候scope配置成SCOPE_PROTOTYPE,则Spring创建的bean是多例的。image它里面包含的逻辑也非常清晰,只有:beforePrototypeCreation、createBean、afterPrototypeCreation和getObjectForBeanInstance,这4个方法。

beforePrototypeCreation方法很简单:image它会将beanName保存到prototypesCurrentlyInCreation当中。image而prototypesCurrentlyInCreation其实是一个ThreadLocal对象。

在afterPrototypeCreation方法中:image将prototypesCurrentlyInCreation中的beanName删除了。

这是一个非常标准的ThreadLocal的用法。

在try中set值,在finally中remove该值,并且ThreadLocal对象定义成private final的。

假如不用ThreadLocal,则需要把prototypesCurrentlyInCreation在createBean方法中,一层层往下传递下去,有可能传递的层级非常非常深,让代码不太好维护。

多例bean的代码跟单例的区别是,这里没有使用Spring的一级缓存,每一次请求,都会调用一次createBean方法创建一次bean实例。

3 自定义Scope

如果bean定义的scope不是单例,也不是多例的,则会走自定义Scope创建bean的逻辑:image

它会先获取bean定义时指定的scopeName,然后从scopes集合中获取相应的Scope对象。imagescopes其实是一个map。

而Scope其实是一个接口:image最主要的是get和remove方法。

我们一起看看哪些类型实现了Scope接口:image其中:

  1. AbstractRequestAttributesScope:作用范围是一个request请求。
  2. SessionScope:作用范围是服务器的session当中。
  3. ServletContextScope:作用范围是整个应用当中。

这里以AbstractRequestAttributesScope为例:image它的get方法中,先从request中获取属性集合,然后从属性集合中,根据name和scope,获取具体的对象。

如果对象为空,则通过objectFactory.getObject方法获取一个对象,重新设置到属性集合中。

最后返回获取的对象。

4 自定义Scope实战

比如,我们想在同一个线程中从spring容器获取到的bean都是同一个对象,该怎么办?

这就需要自定义Scope了。

第一步实现Scope接口:

publicclassThreadLocalScopeimplementsScope{


privatestaticfinalThreadLocalTHREAD_LOCAL_SCOPE=newThreadLocal();


@Override
publicObjectget(Stringname,ObjectFactory<?>objectFactory){
Objectvalue=THREAD_LOCAL_SCOPE.get();
if(value!=null){
returnvalue;
}


Objectobject=objectFactory.getObject();
THREAD_LOCAL_SCOPE.set(object);
returnobject;
}


@Override
publicObjectremove(Stringname){
THREAD_LOCAL_SCOPE.remove();
returnnull;
}


@Override
publicvoidregisterDestructionCallback(Stringname,Runnablecallback){


}


@Override
publicObjectresolveContextualObject(Stringkey){
returnnull;
}


@Override
publicStringgetConversationId(){
returnnull;
}
}

第二步将新定义的Scope注入到spring容器中:

@Component
publicclassThreadLocalBeanFactoryPostProcessorimplementsBeanFactoryPostProcessor{


@Override
publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)throwsBeansException{
beanFactory.registerScope("threadLocalScope",newThreadLocalScope());
}
}

第三步使用新定义的Scope:

@Scope("threadLocalScope")
@Service
publicclassCService{
publicvoidadd(){
}
}

4 adaptBeanInstance方法

在doGetBean方法中,最后有一个比较特殊的方法:adaptBeanInstance。

image它会校验当前的bean实例,跟传入的Class类型是否匹配。

比如:通过getBean(a,B.class);获取bean时,获取到的bean实例的类型是A,则传入的Class是B,这种情况就不匹配,需要报错,提示用户。

先通过getTypeConverter方法获取TypeConverter:image如果用户自定义了typeConverter,则直接使用用户自定义的typeConverter。如果用户没有自定义typeConverter,则使用SimpleTypeConverter。

再调用它的convertIfNecessary做bean和Class是否匹配的校验。

后面会有专题介绍TypeConverter的,敬请期待。

好了,今天的内容先分享到这里,我们下期再见。

未完。。。待续。。。

最后修改:2026 年 06 月 05 日
如果觉得我的文章对你有用,请随意赞赏