【沸腾源码】【python源码库】【漫画分销源码】autowired源码

2024-11-19 01:53:15 来源:社区志愿者系统源码 分类:百科

1.画带你彻底弄懂三级缓存和循环依赖的问题
2.从源码层面带你实现一个自动注入注解
3.@autowired注解有什么用?
4.@Lazy注解源码分析
5.Spring源码Autowired注入流程

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是python源码库可以用来使用的,我们平时在用的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/

从源码层面带你实现一个自动注入注解

       首先,需要了解到的是。SpringBean的生命周期

       在生命周期中。注入bean属性的位置是在以下代码:populateBean位置中

       那么我们在项目中使用注解产生一个bean的时候必定会经过以下代码进行一个bean的创建流程

/**省略代码**///开始初始化bean实例对象ObjectexposedObject=bean;try{ //<5>对bean进行填充,将各个属性值注入,其中,可能存在依赖于其他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注解有什么用?

       最近在审查代码时,注意到一些@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方法,使用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是单例的,也可能出现循环依赖问题。

       如果你对循环依赖问题感兴趣,可以参考我的另一篇专题《》,其中详细介绍了相关内容。

       7. @Autowired与@Resource的区别

       @Autowired功能强大,但也存在一些不足,比如它与Spring强耦合,换用其他框架功能失效。而@Resource是JSR-提供的,是Java标准,大部分框架都支持。

       除此之外,在某些场景下,使用@Autowired无法满足需求,而使用@Resource则能解决问题。接下来,我们来看看@Autowired与@Resource的区别。

       另外,它们的装配顺序不同。

       @Autowired的装配顺序如下:

       @Resource的装配顺序如下:

       后记

       原本计划接着分析@Autowired的原理和源码解读,但由于篇幅过长,不适合合并在一起,我打算另开一个专题。如果你对这个话题感兴趣,请持续关注我后续的文章,相信你一定能从中有所收获。

@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初始化过程中的工作原理,可以帮助开发者更有效地利用该框架的特性,实现更高效的应用开发。

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注入提供一些帮助。写这篇文章我投入了一周的时间,尽管过程艰辛,但如果觉得有价值,请给予鼓励,如点赞、收藏或转发。期待您的宝贵意见,让我们共同进步!

本文地址:http://8o.net.cn/html/39c159998361.html 欢迎转发