写点什么

SpringloC 容器的依赖注入源码解析(3)

  • 2022 年 5 月 16 日
  • 本文字数:5104 字

    阅读完需:约 17 分钟

}


相比单例的 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<String> dependentBeans =


this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));


if (!dependentBeans.add(dependentBeanName)) {


return;


}


}


synchronized (this.dependenciesForBeanMap) {


Set<String> 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 实例的时候就应该已经获取到了。


在并发场景下,两个线程在三级缓存里都没获取 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 到单例,就会来到这里。


再回到上面的 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);


this.singletonFactories.remove(beanName);


this.earlySingletonObjects.remove(beanName);


this.registeredSingletons.add(beanName);


}


}


执行完 addSingleton 之后就会返回完整的 bean 实例了




回到 doGetBean 的创建单例的逻辑中,返回完整的 bean 实例之后就会执行


// 如果是普通 bean,直接返回,是 FactoryBean,返回他的 getObject


bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);


再看下 scope 为 Prototype 的创建:



进入beforePrototypeCreation


protected void beforePrototypeCreation(String beanName) {


Object curVal = this.prototypesCurrentlyInCreation.get();


if (curVal == null) {


this.prototypesCurrentlyInCreation.set(beanName);


}


else if (curVal instanceof String) {


Set<String> beanNameSet = new HashSet<>(2);


beanNameSet.add((String) curVal);


beanNameSet.add(beanName);


this.prototypesCurrentlyInCreation.set(beanNameSet);


}


else {


Set<String> beanNameSet = (Set<String>) curVal;


beanNameSet.add(beanName);


}


}


向类型为 ThreadLocal 的 prototypesCurrentlyInCreation(当前线程正在创建的 Prototype 的 bean 名字列表)去添加该 bean 的记录,防止循环依赖,表示这个 bean 正在创建中。


回到 doGetBean,之后也调用了prototypeInstance = createBean(beanName, mbd, args),创建完之后将先前注册的正在创建中的 Bean 信息给抹除掉

用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
SpringloC容器的依赖注入源码解析(3)_Java_爱好编程进阶_InfoQ写作社区