1.从源码层面带你实现一个自动注入注解
2.注解@Autowired和@Resource的区别总结
3.@Lazy注解源码分析
4.@autowired注解有什么用?
5.画图带你彻底弄懂三级缓存和循环依赖的问题
6.Spring源码Autowired注入流程
从源码层面带你实现一个自动注入注解
首先,需要了解到的是。SpringBean的生命周期在生命周期中。注入bean属性的位置是在以下代码:populateBean位置中
那么我们在项目中使用注解产生一个bean的时候必定会经过以下代码进行一个bean的创建流程
/**省略代码**///开始初始化bean实例对象ObjectexposedObject=bean;try{ //<5>对bean进行填充,将各个属性值注入,其中,左侧导航Html源码可能存在依赖于其他bean的属性populateBean(beanName,mbd,instanceWrapper);//<6>调用初始化方法exposedObject=initializeBean(beanName,exposedObject,mbd);}catch(Throwableex){ if(exinstanceofBeanCreationException&&beanName.equals(((BeanCreationException)ex).getBeanName())){ throw(BeanCreationException)ex;}else{ thrownewBeanCreationException(mbd.getResourceDescription(),beanName,"Initializationofbeanfailed",ex);}}/**省略代码**/在生命周期中populateBean进行填充bean数据。把其他依赖引入进来
BeanPostProcessor是一个bean创建时候的一个钩子。
以下代码是循环调用实现了BeanPostProcessor子类InstantiationAwareBeanPostProcessor#postProcessProperties方法
Spring在以下代码中有自动注入的拓展点。关键就是实现InstantiationAwareBeanPostProcessor#postProcessProperties
/**省略代码**/for(BeanPostProcessorbp:getBeanPostProcessors()){ if(bpinstanceofInstantiationAwareBeanPostProcessor){ InstantiationAwareBeanPostProcessoribp=(InstantiationAwareBeanPostProcessor)bp;//对所有需要依赖检查的属性进行后处理PropertyValuespvsToUse=ibp.postProcessProperties(pvs,bw.getWrappedInstance(),beanName);if(pvsToUse==null){ //从bw对象中提取PropertyDescriptor结果集//PropertyDescriptor:可以通过一对存取方法提取一个属性if(filteredPds==null){ filteredPds=filterPropertyDescriptorsForDependencyCheck(bw,mbd.allowCaching);}pvsToUse=ibp.postProcessPropertyValues(pvs,filteredPds,bw.getWrappedInstance(),beanName);if(pvsToUse==null){ return;}}pvs=pvsToUse;}}/**省略代码**/我们展开来讲一下@Autowired的实现是怎么样的吧:
实现类为AutowiredAnnotationBeanPostProcessor.java
从上面可以得知,填充bean的时候。时调用了方法ibp.postProcessPropertyValues()
那么AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues()则会被调用
调用findAutowiringMetadata获取class以及父类带有@Autowired或者@Value的属性或者方法:
/**省略代码**/publicPropertyValuespostProcessProperties(PropertyValuespvs,Objectbean,StringbeanName){ //获取所有可以注入的元数据InjectionMetadatametadata=findAutowiringMetadata(beanName,bean.getClass(),pvs);try{ //注入数据metadata.inject(bean,beanName,pvs);}catch(BeanCreationExceptionex){ throwex;}catch(Throwableex){ thrownewBeanCreationException(beanName,"Injectionofautowireddependenciesfailed",ex);}returnpvs;}privateInjectionMetadatafindAutowiringMetadata(StringbeanName,Class<?>clazz,@NullablePropertyValuespvs){ //缓存名字获取StringcacheKey=(StringUtils.hasLength(beanName)?beanName:clazz.getName());InjectionMetadatametadata=this.injectionMetadataCache.get(cacheKey);//获取是否已经读取过这个class类的InjectionMetadata有的话直接从缓存中获取出去if(InjectionMetadata.needsRefresh(metadata,clazz)){ synchronized(this.injectionMetadataCache){ //双重检查metadata=this.injectionMetadataCache.get(cacheKey);if(InjectionMetadata.needsRefresh(metadata,clazz)){ if(metadata!=null){ metadata.clear(pvs);}//构建自动注入的元数据metadata=buildAutowiringMetadata(clazz);this.injectionMetadataCache.put(cacheKey,metadata);}}}returnmetadata;}privateInjectionMetadatabuildAutowiringMetadata(finalClass<?>clazz){ if(!AnnotationUtils.isCandidateClass(clazz,this.autowiredAnnotationTypes)){ returnInjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement>elements=newArrayList<>();Class<?>targetClass=clazz;do{ finalList<InjectionMetadata.InjectedElement>currElements=newArrayList<>();//循环targetClass的所有field并执FieldCallback逻辑(函数式编程接口,传入的是一个执行函数)ReflectionUtils.doWithLocalFields(targetClass,field->{ //获得字段上面的Annotation注解MergedAnnotation<?>ann=findAutowiredAnnotation(field);if(ann!=null){ //判断是否为静态属性如果是,则不进行注入if(Modifier.isStatic(field.getModifiers())){ if(logger.isInfoEnabled()){ logger.info("Autowiredannotationisnotsupportedonstaticfields:"+field);}return;}//注解是否为必须依赖项booleanrequired=determineRequiredStatus(ann);currElements.add(newAutowiredFieldElement(field,required));}});//循环targetClass的所有Method并执MethodCallback逻辑(函数式编程接口,传入的是一个执行函数)ReflectionUtils.doWithLocalMethods(targetClass,method->{ MethodbridgedMethod=BridgeMethodResolver.findBridgedMethod(method);if(!BridgeMethodResolver.isVisibilityBridgeMethodPair(method,bridgedMethod)){ return;}MergedAnnotation<?>ann=findAutowiredAnnotation(bridgedMethod);if(ann!=null&&method.equals(ClassUtils.getMostSpecificMethod(method,clazz))){ //判断是否为静态方法如果是,则不进行注入if(Modifier.isStatic(method.getModifiers())){ if(logger.isInfoEnabled()){ logger.info("Autowiredannotationisnotsupportedonstaticmethods:"+method);}return;}//判断静态方法参数是否为0if(method.getParameterCount()==0){ if(logger.isInfoEnabled()){ logger.info("Autowiredannotationshouldonlybeusedonmethodswithparameters:"+method);}}booleanrequired=determineRequiredStatus(ann);PropertyDescriptorpd=BeanUtils.findPropertyForMethod(bridgedMethod,clazz);currElements.add(newAutowiredMethodElement(method,required,pd));}});//数据加到数组最前方父类的的注解都放在靠前的位置elements.addAll(0,currElements);//如果有父类则设置targetClass为父类。如此循环targetClass=targetClass.getSuperclass();}while(targetClass!=null&&targetClass!=Object.class);returnInjectionMetadata.forElements(elements,clazz);}/**省略代码**/真正注入数据的是metadata.inject(bean,beanName,pvs);
调用的是InjectionMetadata#inject方法
publicvoidinject(Objecttarget,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{ Collection<InjectedElement>checkedElements=this.checkedElements;//带有注解的方法或者属性列表Collection<InjectedElement>elementsToIterate=(checkedElements!=null?checkedElements:this.injectedElements);if(!elementsToIterate.isEmpty()){ for(InjectedElementelement:elementsToIterate){ element.inject(target,beanName,pvs);}}}循环调用之前加入的带有注解的方法或者属性构建的对象AutowiredFieldElement#inject,AutowiredMethodElement#inject
/***属性上有注解构建的处理对象*/privateclassAutowiredFieldElementextendsInjectionMetadata.InjectedElement{ privatefinalbooleanrequired;privatevolatilebooleancached;@NullableprivatevolatileObjectcachedFieldValue;publicAutowiredFieldElement(Fieldfield,booleanrequired){ super(field,null);this.required=required;}@Overrideprotectedvoidinject(Objectbean,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{ //获取属性名Fieldfield=(Field)this.member;Objectvalue;//Bean不是单例的话,会重复进入注入的这个操作,if(this.cached){ try{ value=resolvedCachedArgument(beanName,this.cachedFieldValue);}catch(NoSuchBeanDefinitionExceptionex){ //Unexpectedremovaloftargetbeanforcachedargument->re-resolvevalue=resolveFieldValue(field,bean,beanName);}}else{ //首次创建的时候进入该方法value=resolveFieldValue(field,bean,beanName);}if(value!=null){ //属性如果不为public的话,则设置为可访问ReflectionUtils.makeAccessible(field);field.set(bean,value);}}@NullableprivateObjectresolveFieldValue(Fieldfield,Objectbean,@NullableStringbeanName){ //构建DependencyDescriptor对象DependencyDescriptordesc=newDependencyDescriptor(field,this.required);desc.setContainingClass(bean.getClass());//注入bean的数量。有可能字段上是一个ListSet<String>autowiredBeanNames=newLinkedHashSet<>(1);Assert.state(beanFactory!=null,"NoBeanFactoryavailable");//获得beanFactory类型转换类TypeConvertertypeConverter=beanFactory.getTypeConverter();Objectvalue;try{ //查找依赖关系value=beanFactory.resolveDependency(desc,beanName,autowiredBeanNames,typeConverter);}catch(BeansExceptionex){ thrownewUnsatisfiedDependencyException(null,beanName,newInjectionPoint(field),ex);}synchronized(this){ if(!this.cached){ ObjectcachedFieldValue=null;if(value!=null||this.required){ cachedFieldValue=desc;//填入依赖关系registerDependentBeans(beanName,autowiredBeanNames);//判断如果注入依赖是只有一个if(autowiredBeanNames.size()==1){ StringautowiredBeanName=autowiredBeanNames.iterator().next();if(beanFactory.containsBean(autowiredBeanName)&&beanFactory.isTypeMatch(autowiredBeanName,field.getType())){ cachedFieldValue=newShortcutDependencyDescriptor(desc,autowiredBeanName,field.getType());}}}this.cachedFieldValue=cachedFieldValue;this.cached=true;}}returnvalue;}}/***方法上有注解构建的处理对象*/privateclassAutowiredMethodElementextendsInjectionMetadata.InjectedElement{ privatefinalbooleanrequired;privatevolatilebooleancached;@NullableprivatevolatileObject[]cachedMethodArguments;publicAutowiredMethodElement(Methodmethod,booleanrequired,@NullablePropertyDescriptorpd){ super(method,pd);this.required=required;}@Overrideprotectedvoidinject(Objectbean,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{ //检查属性是不会在之前就已经注入过了。如果主如果则不进行二次覆盖if(checkPropertySkipping(pvs)){ return;}Methodmethod=(Method)this.member;Object[]arguments;if(this.cached){ try{ arguments=resolveCachedArguments(beanName);}catch(NoSuchBeanDefinitionExceptionex){ //Unexpectedremovaloftargetbeanforcachedargument->re-resolvearguments=resolveMethodArguments(method,bean,beanName);}}else{ //首次创建的时候进入该方法arguments=resolveMethodArguments(method,bean,beanName);}if(arguments!=null){ try{ //属性如果不为public的话,则设置为可访问ReflectionUtils.makeAccessible(method);//调用方法并传入参数method.invoke(bean,arguments);}catch(InvocationTargetExceptionex){ throwex.getTargetException();}}}@NullableprivateObject[]resolveCachedArguments(@NullableStringbeanName){ Object[]cachedMethodArguments=this.cachedMethodArguments;if(cachedMethodArguments==null){ returnnull;}Object[]arguments=newObject[cachedMethodArguments.length];for(inti=0;i<arguments.length;i++){ arguments[i]=resolvedCachedArgument(beanName,cachedMethodArguments[i]);}returnarguments;}@NullableprivateObject[]resolveMethodArguments(Methodmethod,Objectbean,@NullableStringbeanName){ //获取方法上有几个参数intargumentCount=method.getParameterCount();Object[]arguments=newObject[argumentCount];DependencyDescriptor[]descriptors=newDependencyDescriptor[argumentCount];Set<String>autowiredBeans=newLinkedHashSet<>(argumentCount);Assert.state(beanFactory!=null,"NoBeanFactoryavailable");TypeConvertertypeConverter=beanFactory.getTypeConverter();for(inti=0;i<arguments.length;i++){ //方法参数,从方法参数中取出i构造MethodParameter对象MethodParametermethodParam=newMethodParameter(method,i);DependencyDescriptorcurrDesc=newDependencyDescriptor(methodParam,this.required);currDesc.setContainingClass(bean.getClass());descriptors[i]=currDesc;try{ //获取方法中i参数的内容Objectarg=beanFactory.resolveDependency(currDesc,beanName,autowiredBeans,typeConverter);if(arg==null&注解@Autowired和@Resource的区别总结
@Autowired和@Resource是Spring框架中常见的依赖注入注解,但它们的注入机制和处理略有不同。接下来,我们将从源码角度深入剖析它们的注入过程。
@Autowired总结:
- 注入流程涉及AutowiredAnnotationBeanPostProcessor,首先检查属性或方法上的@Autowired,构建AutowiredFieldElement或AutowiredMethodElement。
- 如果未启用懒加载,AutowiredFieldElement会通过DefaultListableBeanFactory的resolveDependency方法寻找并注入bean,包括候选bean的查找和确定。
@Resource总结:
- CommonAnnotationBeanPostProcessor的buildResourceMetadata方法是切入点,只对非静态、非忽略类型的字段创建ResourceElement对象。
- ResourceElement对象的getResourceToInject方法负责获取bean,通过autowireResource方法调用。
源码分析:
1. @Autowired的注入过程涉及AutowiredAnnotationBeanPostProcessor的多个内部方法,如doResolveDependency和findAutowireCandidates,处理了候选bean的选择和懒加载机制。
2. @Resource的流程在CommonAnnotationBeanPostProcessor中更为直接,主要通过ResourceElement类的getResourceToInject方法获取bean。
学习更多关于Java和Spring的深入知识,如MyBatis、ZooKeeper等,持续关注博主的更新。
@Lazy注解源码分析
@Lazy注解是Spring框架3.0版本后引入的,用于控制bean的懒加载行为,主要用途是延迟依赖注入的初始化。默认情况下,当ApplicationContext启动和刷新时,所有的单例bean会被立即初始化。然而,有时可能希望某些bean在首次使用时才被初始化。实现这一目标的方法是将@Lazy注解应用到bean或注入点,如@Autowired,以创建懒解析代理,从而实现延迟注入。
@Lazy注解对@Bean、@Component或@Bean定义的bean的延迟初始化特别有用。当用在@Configuration类上时,它会影响该配置中的挂机投注源码所有@Bean定义。通过在启动类入口使用AnnotationConfigApplicationContext并提供MyConfiguration组件类,从MyService bean获取并调用其show方法,可以观察到MyBean在首次被请求时才被初始化,而MyService的初始化则立即进行。MyBean类的构造函数在被调用时打印"MyBean的构造函数被调用了!",show方法则打印"hello world!"。MyService类通过@Autowired注入MyBean,由于在注入点上添加了@Lazy注解,myBean的实际注入被延迟,直到首次尝试访问它时。
源码分析表明,在启动类构造函数中,执行了三个步骤以初始化实例。在refresh方法中,重点关注了finishBeanFactoryInitialization方法,该方法会对所有剩余非懒加载的单例bean对象进行初始化,除非它们显式标记为懒加载。在preInstantiateSingletons方法中,确保所有非懒加载的单例bean在容器启动时被初始化,除非它们被标记为懒加载。这使得@Lazy注解对于希望推迟bean初始化的场景非常有用。
在getBean()方法中,通过doGetBean方法执行了创建bean的过程。在doCreateBean方法中,对bean的属性进行注入。在populateBean方法中,如果一个属性被标记为@Autowired,并且与@Lazy结合使用,那么实际的懒加载逻辑会在其他部分处理,特别是通过AutowiredAnnotationBeanPostProcessor。在resolveFieldValue方法中,解析@Autowired字段的值,并确定应为目标字段注入哪个bean。在resolveDependency方法中,如果依赖关系标记为懒加载,它将返回一个懒加载代理,只有在应用程序真正访问该依赖时,实际的bean才会被初始化。
总结而言,@Lazy注解提供了在Spring容器中控制bean初始化的灵活性,允许开发者根据需要延迟依赖注入的初始化,从而优化应用性能和资源管理。在实践过程中,注意合理使用@Lazy注解,确保代码的清晰性和可维护性。同时,理解Spring容器在bean初始化过程中的工作原理,可以帮助开发者更有效地利用该框架的特性,实现更高效的应用开发。
@autowired注解有什么用?
最近在审查代码时,注意到一些@Autowired注解的非典型用法,觉得颇为有趣,于是花时间深入研究,发现了不少有价值的信息,现在与大家分享。 @Autowired可能比你想象的更为强大。1. 默认装配方式
我们知道,在Spring中,@Autowired注解主要用于自动装配对象。通常,我们在项目中这样使用: 是的,这样做确实能够实现装配,因为默认情况下,简易小说源码Spring会按照类型进行装配,即byType方式。 另外,@Autowired注解的required参数默认设为true,表示开启自动装配,若不希望使用自动装配功能,可通过将其设为false来实现。2. 相同类型的对象不止一个时
byType方式主要针对对象类型唯一的情况,当对象类型不唯一时,会出现问题。 例如,在项目的test目录下创建了一个名为TestService1的类。重启项目时,会报错,提示类名冲突,导致项目无法启动。 需要注意的是,这种情况与在@Autowired时出现两个相同类型的对象无关,容易引起混淆。问题是由于Spring的@Service方法不允许出现相同类名,而类名首字母转换为小写作为bean名称,如testService1,且默认情况下bean名称必须唯一。3. 使用@Qualifier和@Primary
显然,按照默认的byType装配方式,无法解决上述问题,此时可改用byName装配方式。 只需在代码中添加@Qualifier注解即可解决。 只需进行这样的调整,项目就能正常启动。 @Qualifier意味着“合格者”,通常与@Autowired结合使用,通过指定bean名称来找到需要装配的bean。 除了使用@Qualifier注解外,还可以使用@Primary注解解决相同类型bean的问题。在User1上加上@Primary注解,移除UserService上的@Qualifier注解。 重新启动项目,同样能正常运行。 当使用自动配置方式装配Bean时,若有多个候选者,其中一个带有@Primary注解,该候选者会被选中作为自动配置的值。4. @Autowired的使用范围
在成员变量上使用@Autowired注解是常见的用法。除此之外,它还能应用于其他场景。 下面总结一下@Autowired注解的使用方式:4.1 成员变量
在成员变量上使用@Autowired注解是常见用法。4.2 构造器
在构造器上使用@Autowired注解,实际上还是使用了@Autowired装配方式,而非构造器装配。4.3 方法
在普通方法上添加@Autowired注解,spring会在项目启动时调用一次该方法,我们可在该方法中执行初始化工作。 同样可以在setter方法上使用@Autowired注解。4.4 参数
构造器的入参上可使用@Autowired注解,非静态方法的入参上也可使用。4.5 注解
使用@Autowired注解的注解实例相对较少,因此这里不再过多介绍。5. @Autowired的高级玩法
虽然上面的例子都是自动装配单个实例,但事实上,它也能自动装配多个实例。让我们看看是怎么实现的。 调整UserService方法,源码升级php使用List集合接收IUser类型的参数。 增加一个controller,调用该接口。 通过观察结果,可以发现userList、userSet和userMap都打印出了两个元素,说明@Autowired会自动收集相同类型的IUser对象到集合中。 这种功能令人惊讶,令人惊喜!6. @Autowired装配是否一定成功?
前面介绍了@Autowired注解的强大功能,但有些情况下,即使使用了@Autowired仍然装配失败,这是为什么呢?6.1 没有加@Service注解
在类上忘记添加@Controller、@Service、@Component、@Repository等注解,Spring将无法完成自动装配功能,例如: 这种错误是最常见的,不会因为你长得帅,就避免犯这种低级错误。6.2 注入Filter或Listener
Web应用启动顺序为:listener->filter->servlet。 接下来我们看看这个案例: 程序启动会报错,提示tomcat无法正常启动。 原因是什么? 众所周知,springmvc的启动在DisptachServlet中完成,而它是在listener和filter之后执行。如果在listener和filter中使用@Autowired注入某个bean,肯定不行,因为filter初始化时,此时bean尚未初始化,无法自动装配。 如果在工作中真的需要这样做,该如何解决呢? 答案是使用WebApplicationContextUtils.getWebApplicationContext获取当前的ApplicationContext,再通过它获取bean实例。6.3 注解未被@ComponentScan扫描
通常情况下,@Controller、@Service、@Component、@Repository、@Configuration等注解需要通过@ComponentScan注解进行扫描,收集元数据。 但如果没有添加@ComponentScan注解,或其扫描路径错误或范围过小,可能导致注解收集不全,进而无法使用@Autowired完成自动装配。 好消息是,在springboot项目中,使用了@SpringBootApplication注解内置了@ComponentScan的功能。6.4 循环依赖问题
如果A依赖于B,B依赖于C,C又依赖于A,形成死循环。 Spring的bean默认是单例的,大多数情况下,能解决循环依赖问题。 但若bean是多例的,会引发循环依赖问题,导致自动装配失败。 还有些情况下,即使创建了代理对象,即使bean是单例的,也可能出现循环依赖问题。 如果你对循环依赖问题感兴趣,hello uniapp源码可以参考我的另一篇专题《》,其中详细介绍了相关内容。7. @Autowired与@Resource的区别
@Autowired功能强大,但也存在一些不足,比如它与Spring强耦合,换用其他框架功能失效。而@Resource是JSR-提供的,是Java标准,大部分框架都支持。 除此之外,在某些场景下,使用@Autowired无法满足需求,而使用@Resource则能解决问题。接下来,我们来看看@Autowired与@Resource的区别。 另外,它们的装配顺序不同。 @Autowired的装配顺序如下: @Resource的装配顺序如下:后记
原本计划接着分析@Autowired的原理和源码解读,但由于篇幅过长,不适合合并在一起,我打算另开一个专题。如果你对这个话题感兴趣,请持续关注我后续的文章,相信你一定能从中有所收获。画图带你彻底弄懂三级缓存和循环依赖的问题
大家好。我们都知道,Spring可以通过三级缓存解决循环依赖的问题,这也是面试中很常见的一个面试题,本文就来着重讨论一下有关循环依赖和三级缓存的问题。一、什么是循环依赖大家平时在写业务的时候应该写过这样的代码。
其实这种类型就是循环依赖,就是AService 和BService两个类相互引用。
二、三级缓存可以解决的循环依赖场景如上面所说,大家平时在写这种代码的时候,项目其实是可以起来的,也就是说其实三级缓存是可以解决这种循环依赖的。
当然除了这种字段注入,set注入也是可以解决的,代码如下。
接下来就来探究三级缓存是如何解决这种循环依赖的?
三、Spring的Bean是如何创建出来的本文所说的Bean和对象可以理解为同一个意思。
先说如何解决循环依赖之前,先来了解一下一个Bean创建的大致流程。为什么要说Bean的创建过程,因为循环依赖主要是发生在Bean创建的过程中,知道Bean是如何创建的,才能更好的理解三级缓存的作用。
其实Spring Bean的生命周期源码剖析我也在微信公众号 三友的java日记 中发过,并且有简单的提到三级缓存,有兴趣的同学可以在关注公众号之后回复 Bean 即可获取文章链接,里面有Bean创建过程更详细的说明。这里我简单画一张图来说一下。
其实图里的每个阶段还可以分为一些小的阶段,我这里就没画出来了。
来说一下每个阶段干了什么事。
BeanDefinition的读取阶段:我们在往Spring容器注入Bean的时候,一般会通过比如xml方式,@Bean注解的方式,@Component注解的方式,其实不论哪一种,容器启动的时候都会去解析这些配置,然后为每个Bean生成一个对应的BeanDefinition,这个BeanDefinition包含了这个Bean的创建的信息,Spring就是根据BeanDefinition去决定如何创建一个符合你要求的Bean
Bean的实例化阶段:这个阶段主要是将你配置的Bean根据Class的类型创建一个对象出来
Bean的属性赋值阶段:这个阶段主要是用来处理属性的赋值,比如@Autowired注解的生效就是在这个阶段的
Bean的初始化阶段:这个阶段主要是回调一些方法,比如你的类实现了InitializingBean接口,那么就会回调afterPropertiesSet方法,同时动态代理其实也是在这个阶段完成的。
其实从这可以看出,一个Spring Bean的生成要分为很多的阶段,只有这些事都处理完了,这个Bean才是完完全全创建好的Bean,也就是我们可以使用的Bean。
四、三级缓存指的是哪三级缓存这里直接上源码
第一级缓存:singletonObjects
存放已经完完全全创建好的Bean,什么叫完完全全创建好的?就是上面说的是,所有的步骤都处理完了,就是创建好的Bean。一个Bean在产的过程中是需要经历很多的步骤,在这些步骤中可能要处理@Autowired注解,又或是处理@Transcational注解,当需要处理的都处理完之后的Bean,就是完完全全创建好的Bean,这个Bean是可以用来使用的,我们平时在用的Bean其实就是创建好的。
第二级缓存:earlySingletonObjects
早期暴露出去的Bean,其实也就是解决循环依赖的Bean。早期的意思就是没有完完全全创建好,但是由于有循环依赖,就需要把这种Bean提前暴露出去。其实 早期暴露出去的Bean 跟 完完全全创建好的Bean 他们是同一个对象,只不过早期Bean里面的注解可能还没处理,完完全全的Bean已经处理了完了,但是他们指的还是同一个对象,只不过它们是在Bean创建过程中处于的不同状态,如果早期暴露出去的Bean跟完完全全创建好的Bean不是同一个对象是会报错的,项目也就起不来,这个不一样导致报错问题,这里我会结合一个案例再来写一篇文章,这里不用太care,就认为是一样的。
第三级缓存:singletonFactories
存的是每个Bean对应的ObjectFactory对象,通过调用这个对象的getObject方法,就可以获取到早期暴露出去的Bean。
注意:这里有个很重要的细节就是三级缓存只会对单例的Bean生效,像多例的是无法利用到三级缓存的,通过三级缓存所在的类名DefaultSingletonBeanRegistry就可以看出,仅仅是对SingletonBean也就是单例Bean有效果。
五、三级缓存在Bean生成的过程中是如何解决循环依赖的这里我假设项目启动时先创建了AService的Bean,那么就会根据Spring Bean创建的过程来创建。
在Bean的实例化阶段,就会创建出AService的对象,此时里面的@Autowired注解是没有处理的,创建出AService的对象之后就会构建AService对应的一个ObjectFactory对象放到三级缓存中,通过这个ObjectFactory对象可以获取到AService的早期Bean。
然后AService继续往下走,到了某一个阶段,开始处理@Autowired注解,要注入BService对象,如图
要注入BService对象,肯定要去找BService对象,那么他就会从三级缓存中的第一级缓存开始依次查找有没有BService对应的Bean,肯定都没有啊,因为BService还没创建呢。没有该怎么办呢?其实很好办,没有就去创建一个么,这样不就有了么。于是AService的注入BService的过程就算暂停了,因为现在得去创建BService,创建之后才能注入给AService。
于是乎,BService就开始创建了,当然他也是Spring的Bean,所以也按照Bean的创建方式来创建,先实例化一个BService对象,然后缓存对应的一个ObjectFactory到第三级缓存中,然后就到了需要处理@Autowired注解的时候了,如图。
@Autowired注解需要注入AService对象。注入AService对象,就需要先去拿到AService对象,此时也会一次从三级缓存查有没有AService。
先从第一级查,有没有创建好的AService,肯定没有,因为AService此时正在在创建(因为AService在创建的过程中需要注入BService才去创建BService的,虽然此刻代码正在创建BService,但是AService也是在创建的过程中,只不过暂停了,只要BService创建完,AService会继续往下创建);第一级缓存没有,那么就去第二级看看,也没有,没有早期的AService;然后去第三级缓存看看有没有AService对应的ObjectFactory对象,惊天的发现,竟然有(上面提到过,创建出AService的对象之后,会构建AService对应的一个ObjectFactory对象放到三级缓存中),那么此时就会调用AService对应的ObjectFactory对象的getObject方法,拿到早期的AService对象,然后将早期的AService对象放到二级缓存,为什么需要放到二级缓存,主要是怕还有其他的循环依赖,如果还有的话,直接从二级缓存中就能拿到早期的AService对象。
虽然是早期的AService对象,但是我前面说过,仅仅只是早期的AService对象可能有些Bean创建的步骤还没完成,跟最后完完全全创建好的AService Bean是同一个对象。
于是接下来就把早期的AService对象注入给BService。
此时BService的@Autowired注解注入AService对象就完成了,之后再经过其他阶段的处理之后,BService对象就完完全全的创建完了。
BService对象创建完之后,就会将BService放入第一级缓存,然后清空BService对应的第三级缓存,当然也会去清空第二级缓存,只是没有而已,至于为什么清空,很简单,因为BService已经完全创建好了,如果需要BService那就在第一级缓存中就能查找到,不需要在从第二级或者第三级缓存中找到早期的BService对象。
BService对象就完完全全的创建完之后,那么接下来该干什么呢?此时当然继续创建AService对象了,你不要忘了为什么需要创建BService对象,因为AService对象需要注入一个BService对象,所以才去创建BService的,那么此时既然BService已经创建完了,那么是不是就应该注入给AService对象了?所以就会将BService注入给AService对象,这下就明白了,BService在构建的时候,已经注入了AService,虽然是早期的AService,但的确是AService对象,现在又把BService注入给了AService,那么是不是已经解决了循环依赖的问题了,AService和BService都各自注入了对方,如图。
然后AService就会跟BService一样,继续处理其它阶段的,完全创建好之后,也会清空二三级缓存,放入第一级缓存。
到这里,AService和BService就都创建好了,循环依赖也就解决了。
这下你应该明白了三级缓存的作用,主要是第二级和第三级用来存早期的对象,这样在有循环依赖的对象,就可以注入另一个对象的早期状态,从而达到解决循环依赖的问题,而早期状态的对象,在构建完成之后,也就会成为完完全全可用的对象。
六、三级缓存无法解决的循环依赖场景1)构造器注入无法解决循环依赖上面的例子是通过@Autowired注解直接注入依赖的对象,但是如果通过构造器注入循环依赖的对象,是无法解决的,如代码下
构造器注入就是指创建AService对象的时候,就传入BService对象,而不是用@Autowired注解注入BService对象。
运行结果
启动时就会报错,所以通过构造器注入对象就能避免产生循环依赖的问题,因为如果有循环依赖的话,那么就会报错。
至于三级缓存为什么不能解决构造器注入的问题呢?其实很好理解,因为上面说三级缓存解决循环依赖的时候主要讲到,在AService实例化之后,会创建对应的ObjectFactory放到第三级缓存,发生循环依赖的时候,可以通过ObjectFactory拿到早期的AService对象;而构造器注入,是发生在实例化的时候,此时还没有AService对象正在创建,还没完成,压根就还没执行到往第三级添加对应的ObjectFactory的步骤,那么BService在创建的时候,就无法通过三级缓存拿到早期的AService对象,拿不到怎么办,那就去创建AService对象,但是AService不是正在创建么,于是会报错。
2)注入多例的对象无法解决循环依赖**启动引导类
要获取AService对象,因为多例的Bean在容器启动的时候是不会去创建的,所以得去获取,这样就会创建了。
运行结果
为什么不能解决,上面在说三级缓存的时候已经说过了,三级缓存只能对单例Bean生效,那么多例是不会起作用的,并且在创建Bean的时候有这么一个判断,那就是如果出现循环依赖并且是依赖的是多例的Bean,那么直接抛异常,源码如下
注释其实说的很明白,推测出现了循环依赖,抛异常。
所以上面提到的两种循环依赖的场景,之所以无法通过三级缓存来解决,是因为压根这两种场景就无法使用三级缓存,所以三级缓存肯定解决不掉。\
七、不用三级缓存,用二级缓存能不能解决循环依赖遇到这种面试题,你就跟面试官说,如果行的话,Spring的作者为什么不这么写呢?
哈哈,开个玩笑,接下来说说到底为什么不行。
这里我先说一下前面没提到的细节,那就是通过ObjectFactory获取的Bean可能是两种类型,第一种就是实例化阶段创建出来的对象,还是一种就是实例化阶段创建出来的对象的代理对象。至于是不是代理对象,取决于你的配置,如果添加了事务注解又或是自定义aop切面,那就需要代理。这里你不用担心,如果这里获取的是代理对象,那么最后完全创建好的对象也是代理对象,ObjectFactory获取的对象和最终完全创建好的还是同一个,不是同一个肯定会报错,所以上面的理论依然符合,这里只是更加的细节化。
有了这个知识点之后,我们就来谈一下为什么要三级缓存。
第一级缓存,也就是缓存完全创建好的Bean的缓存,这个缓存肯定是需要的,因为单例的Bean只能创建一次,那么肯定需要第一级缓存存储这些对象,如果有需要,直接从第一级缓存返回。那么如果只能有二级缓存的话,就只能舍弃第二级或者第三级缓存。
假设舍弃第三级缓存
舍弃第三级缓存,也就是没有ObjectFactory,那么就需要往第二缓存放入早期的Bean,那么是放没有代理的Bean还是被代理的Bean呢?
1)如果直接往二级缓存添加没有被代理的Bean,那么可能注入给其它对象的Bean跟最后最后完全生成的Bean是不一样的,因为最后生成的是代理对象,这肯定是不允许的;
2)那么如果直接往二级缓存添加一个代理Bean呢?
假设没有循环依赖,提前暴露了代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错,
假设没有循环依赖,使用了ObjectFactory,那么就不会提前暴露了代理对象,到最后生成的对象是什么就是什么,就不会报错,
如果有循环依赖,不论怎样都会提前暴露代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错
通过上面分析,如果没有循环依赖,使用ObjectFactory,就减少了提前暴露代理对象的可能性,从而减少报错的可能。
假设舍弃第二级缓存
假设舍弃第二级缓存,也就是没有存放早期的Bean的缓存,其实肯定也不行。上面说过,ObjectFactory其实获取的对象可能是代理的对象,那么如果每次都通过ObjectFactory获取代理对象,那么每次都重新创建一个代理对象,这肯定也是不允许的。
从上面分析,知道为什么不能使用二级缓存了吧,第三级缓存就是为了避免过早地创建代理对象,从而避免没有循环依赖过早暴露代理对象产生的问题,而第二级缓存就是防止多次创建代理对象,导致对象不同。
本文完。
如果觉得这篇文章对你有所帮助,还请帮忙点赞、在看、转发给更多的人,码字不易,非常感谢!
欢迎关注公众号 三友的java日记,更多技术干货及时获得。原文:/post/Spring源码Autowired注入流程
在Spring框架中,Autowired注解的注入流程是一个开发者常问的问题。本文将带你深入了解这一过程,基于jdk1.8和spring5.2.8.RELEASE环境。
首先,当Spring应用启动,通过SpringApplication的run方法调用refreshContext,进而执行refresh方法,初始化上下文容器。在这个过程中,非懒加载的bean实例化由finishBeanFactoryInitialization方法负责,特别是其内部的beanFactory.preInstantiateSingletons方法。
在默认非单例bean的getBean方法中,会调用AbstractAutowireCapableBeanFactory的createBean方法,这个方法会处理包括@Autowired在内的各种注解。特别关注AutowiredAnnotationBeanPostProcessor,它在获取元数据后,会进入beanFactory.resolveDependency来处理可能的多个依赖问题。
最后,DefaultListableBeanFactory的doResolveDependency方法通过反射机制,实现了属性注入。尽管这只是整个流程的概述,但深入源码可以帮助我们更好地理解Autowired的底层工作机制。
虽然这只是一个基本的梳理,但希望能为理解Spring的Autowired注入提供一些帮助。写这篇文章我投入了一周的时间,尽管过程艰辛,但如果觉得有价值,请给予鼓励,如点赞、收藏或转发。期待您的宝贵意见,让我们共同进步!
2024-11-25 01:10942人浏览
2024-11-25 01:012364人浏览
2024-11-25 00:561698人浏览
2024-11-25 00:072446人浏览
2024-11-25 00:052246人浏览
2024-11-24 23:44549人浏览
1.在源码之家了一个PHP网站的源码不知道如何使用2.PHP云进销存管理系统,中小微企业ERP电商源码3.禾匠榜店小程序商城后台源码安装教程最详版)4.ThinkPHP5.1 源码分析四)- 门面Fa
1.��������Դ��2.Python实现五子棋:人机对战 / 人人对战动图演示+源码分享)3.阿尔法元之五子棋源码解读(AlphaZero-Gomoku)��������Դ�� 这次我们
1.食品溯源法律的发展食品溯源法律的发展 食品溯源法律的发展是一个不断进步和完善的过程,旨在确保食品安全、保护消费者权益以及促进食品产业的健康发展。 首先,食品溯源法律的起源可以追溯到较早的时