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.
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());
}
}
return (T) bean;
}
之前讨论了doGetBean里面从缓存获取bean的代码,doGetBean方法接下来的else处理Bean的scope为prototype或者单例模式但是缓存中还不存在bean的情况:
Spring同样为scope为prototype的Bean设计了一个缓存列表
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
相比单例的set,这里是ThreadLocal
,只记录当前线程创建出来的scope为prototype的Bean,上面的if如果是true的话证明有循环依赖。
通过了循环依赖校验之后看容器是否存在父容器,如果存在且当前容器里没有包含此bean的BeanDefinition实例,尝试去从父类容器递归查询
为了防止之前的beanName已经被转换的不成样子,将&重新加上,再调用父类的doGetBean或者getBean方法,如果父类是AbstractBeanFactory,则调用doGetBean
如果当前容器里包含了此bean的BeanDefinition实例则继续执行
// typeCheckOnly 是用来判断调用 getBean() 是否仅仅是为了类型检查获取 bean,而不是为了创建Bean
if (!typeCheckOnly) {
// 如果不是仅仅做类型检查则是创建bean
markBeanAsCreated(beanName);
}
进入markBeanAsCreated方法里,
protected void markBeanAsCreated(String beanName) {
// 双重检查锁机制
if (!this.alreadyCreated.contains(beanName)) {
synchronized (this.mergedBeanDefinitions) {
if (!this.alreadyCreated.contains(beanName)) {
// Let the bean definition get re-merged now that we’re actually creating
// the bean… just in case some of its metadata changed in the meantime.
// 将原先合并之后的RootBeanDefinition的需要重新合并的状态设置为true
// 表示需要重新合并一遍,以防原数据的改动
clearMergedBeanDefinition(beanName);
// 将已经创建好的或者正在创建的Bean的名称加到alreadyCreated这个缓存中
this.alreadyCreated.add(beanName);
}
}
}
}
有一个双重锁检查机制,创建的Bean的名称加到alreadyCreated(类型Set)这个缓存中,在加入缓存之前需要将原先的MergedBeanDefinition
设置上一个需要清除的标识符,目的是让后续从容器中获取BeanDefinition时重新合并子类和父类的BeanDefinition,这样就可以防止元数据被改动后,BeanDefinition还是按照原来的数据去创建
protected void clearMergedBeanDefinition(String beanName) {
RootBeanDefinition bd = this.mergedBeanDefinitions.get(beanName);
if (bd != null) {
bd.stale = true;
}
}
clearMergedBeanDefinition回去容器中获取RootBeanDefinition实例,然后把该实例需要重新合并的状态设为true(之前提到过,只要指定了parent属性,则两个BeanDefinition就合并成一个来使用)
回到doGetBean,接下来会调用getMergedLocalBeanDefinition
方法来合并子类和父类的BeanDefinition,进入到该方法里:
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null && !mbd.stale) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
先从mergedBeanDefinitions缓存里获取之前已经合并好的RootBeanDefinition实例,如果stale为true的话就会合并一遍BeanDefinition,随后返回。
回到doGetBean,获取到了BeanDefinition之后,就去对相关实例做合法性校验
checkMergedBeanDefinition(mbd, beanName, args);
进入到checkMergedBeanDefinition方法里:
protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, @Nullable Object[] args)
throws BeanDefinitionStoreException {
if (mbd.isAbstract()) {
throw new BeanIsAbstractException(beanName);
}
}
看一下RootBeanDefinition是否是抽象的。
回到doGetBean,尝试从BeanDefinition里获取显式的依赖关系,如果有depends-on的话就检查是否有循环依赖关系
如果没有循环依赖的话则会将相关的依赖关系注册上
registerDependentBean(dep, beanName);
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
synchronized (this.dependentBeanMap) {
// computeIfAbsent:若key对应的value为空,会将第二个参数的返回值存入并返回
// dependentBeanMap中存放着当前Bean被引用的Bean的集合
// 比如当前需要实例化的是Bean的名字是userInfo,userInfo中有个Human类型的属性human
// 那么就有human被userInfo引用的关系 human=[userInfo]
Set dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
if (!dependentBeans.add(dependentBeanName)) {
return;
}
}
synchronized (this.dependenciesForBeanMap) {
Set dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}
先获取name,之后就进入两重注册,第一重注册往dependentBeanMap
中写入键值对,key是被依赖的Bean名字,value是依赖他的Bean名字列表
第二重注册往dependenciesForBeanMap
中写入键值对,键值对和上面正好是相反的
回到doGetBean,执行完显式依赖关系注册之后就会递归调用getBean(dep)
来将依赖的bean创建出来,往后就是根据不同的scope进行不同的创建bean的操作了,分为Singleton、Prototype和其他。
除了Prototype是直接调用createBean(beanName, mbd, args)
去创建Bean实例之外,scope=其他的将createBean封装到了一个匿名参数里
进入scope.get()方法
Object get(String name, ObjectFactory<?> objectFactory);
可见后面的匿名函数实现的是ObjectFactory的getObject方法,调用createBean方法去创建适配scope的实例。
下面主要分析scope = singleton的:
if (mbd.isSingleton()) {
// 这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
// 显式从单例缓存中删除 bean实例
// 因为单例模式下为了解决循环依赖,可能它已经存在了,所以将其销毁
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
进入getSingleton方法:
也是接收一个ObjectFactory
对象然后实现其getObject方法。
进入createBean:
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException;
这是个抽象方法需要子类去实现。
再回到上面的getSingleton方法:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, “Bean name must not be null”);
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
“(Do not request a bean from a BeanFactory in a destroy method implementation!)”);
}
if (logger.isDebugEnabled()) {
logger.debug(“Creating shared instance of singleton bean '” + beanName + “'”);
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
先将一级缓存singletonObjects
上锁,获取到锁之后再次尝试从一级缓存里去获取实例,以防别的线程创建好了,如果获取不到实例就开始做真正的创建了:
先看一下目前容器是否正在销毁所有的单例:
singletonsCurrentlyInDestruction
是个flag。
通过验证之后会来到beforeSingletonCreation(beanName)
,进入:
protected void beforeSingletonCreation(String beanName) {
//inCreationCheckExclusions 直接缓存当前不能加载的bean,
//主要用在web容器的拦截器里,所以这里可以忽略,因为肯定是不存在的
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
if 的第一个条件可以忽略,第二个条件是尝试将beanName放入正在创建的单例名字列表里,添加失败则会抛异常,因为bean的正常创建流程按理是进入不到这里的,如果beanName出现在正在创建的名字列表中则表明在本次操作之前对于同一个bean的创建已经在进行了,doGetBean的时候第一步从三级缓存中获取Bean实例的时候就应该已经获取到了。
在并发场景下,两个线程在三级缓存里都没获取到单例,就会来到这里。
再回到上面的getSingleton方法,接下来就可以去创建并获取bean实例了:
newSingleton
设置为true方便后续调用addSingleton
来添加一级缓存。
异常处理跳过,看一下finally:
进入afterSingletonCreation:
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException(“Singleton '” + beanName + “’ isn’t currently in creation”);
}
}
由于bean实例已经创建完毕了,所以会从正在创建的bean名字列表singletonsCurrentlyInCreation
中移除beanName
回到外面,接下来就会去判断是否是新创建的单例,之前标志位已经是true了,进入addSingleton
:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// Bean实例完成创建之后,只保留一级缓存以及注册beanName的顺序,其余的清除
this.singletonObjects.put(beanName, singletonObject);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
最后
这份清华大牛整理的进大厂必备的redis视频、面试题和技术文档
祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!
感谢大家的支持!!
报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-xRtylNAw-1712112376554)]
[外链图片转存中…(img-0I56G5zw-1712112376555)]
[外链图片转存中…(img-kSqVVyfR-1712112376555)]
[外链图片转存中…(img-zyzx0fpC-1712112376555)]
[外链图片转存中…(img-dTfoyLsG-1712112376556)]
[外链图片转存中…(img-8U0IRTLJ-1712112376556)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-KhLhCYOU-1712112376556)]
最后
这份清华大牛整理的进大厂必备的redis视频、面试题和技术文档
祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!
感谢大家的支持!!
[外链图片转存中…(img-RLyGELrj-1712112376557)]