全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-12-29_深入剖析Spring自动注入的实现原理

您的位置:首页 >> 新闻 >> 行业资讯

深入剖析Spring自动注入的实现原理 点击关注公众号,“技术干货”及时达! 摘要:从源码角度剖析@Autowire注解实现自动注入的原理,透彻理解Spring中依赖注入实现的底层逻辑。?前言在日常开发中,@Autowire注解可以说是我们日常开发中使用最频繁的一个注解。通过@Autowire注解我们可以顺利实现相关Bean的自动注入,而无需我们那在手动通过new关键字来构建一个对象。 但最熟悉的往往是最陌生的,不妨思考一下,虽然@Autowire是我们开发中使用最频繁的一个,但你是否明白其内部的原理呢?换言之,你是否有真正了解过@Autowired注解背后的实现原理? 不了解也没关系,本文会重点对@Autowired原理进行深入剖析。相信读完本文能加深你对于 @Autowired注解底层原理有深刻认识。 @Autowire简介@Autowired 注解是Spring 框架中的一种依赖注入机制,用于自动装配 Bean。它可以应用于构造函数、字段和方法上。当 Spring容器启动时,它会自动寻找与被注入类型匹配的 Bean,并将其注入到标注了 @Autowired 的位置。 具体来看,其可用于:「构造器注入,字段注入,方法注入」等。其使用方式如下: 「构造器注入」:在类的构造函数中使用 @Autowired。Spring会在创建实例时注入所需的依赖。其使用如下: @Component publicclassMyService{ privatefinalMyRepositoryrepository; @Autowired publicMyService(MyRepositoryrepository){ this.repository=repository; } } 「字段注入」:直接在字段上使用 @Autowired。虽然代码更简洁,但不利于单元测试,因为需要通过反射或其他方式进行注入。 @Component publicclassMyService{ @Autowired privateMyRepositoryrepository; } 「方法注入」:在 setter 方法上使用 @Autowired,可以实现可选的依赖注入,适合需要修改依赖的场景。 @Component publicclassMyService{ privateMyRepositoryrepository; @Autowired publicvoidsetRepository(MyRepositoryrepository){ this.repository=repository; } } 总之,@Autowired 注解是 Spring框架中用于自动装配 Bean的关键工具。它可以应用于构造器、字段和方法,简化了依赖注入的过程,增强了代码的灵活性与可维护性。 @Autowire解析原理众所周知,在容器启动阶段Spring首先会实例化 Bean,然后进行初始化操作。在初始化阶段,而通过调用Bean 后置处理器可以完成对Bean属性的赋值等操作。进一步,对于 @Autowired而言,其在功能的实现上也依赖于——AutowiredAnnotationBeanPostProcessor这一后置处理器。 具体来看,AutowiredAnnotationBeanPostProcessor 是 Spring框架中内置的一个后置处理器,主要负责处理标注了 @Autowired 注解标注属性的注入。「其主要在Bean的初始化过程中介入,以实现依赖注入。」 更进一步,在 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 会被注册为一个后置处理器。每当一个Bean被创建并初始化时,它会在 postProcessProperties 方法中进行扫描,识别并注入标注了 @Autowired 的属性。通过这一机制,AutowiredAnnotationBeanPostProcessor 有效地实现了 Spring 的依赖注入,提升了代码的灵活性和可维护性。 接下来,我们便来AutowiredAnnotationBeanPostProcessor看看这个类的源码究竟是如何来完成对@Autowire注解进行解析的。 @Autowire解析的时机下图展示了Spring中一个Bean的加载全过程,具体来看,Spring在创建bean的过程中,岂会调用AbstractAutowireCapableBeanFactory中的doCreateBean方法,进而在doCreateBea中进一步调用populateBean()方法完成Bean中属性的填充,而Bean中所依赖bean信息自动装配也全部在populateBean()完成。 其中,populateBean()方法的部分源码如下。 protectedvoidpopulateBean(StringbeanName,RootBeanDefinitionmbd,@NullableBeanWrapperbw){ //...省略无关代码 PropertyDescriptor[]filteredPds=null; if(hasInstAwareBpps){ if(pvs==null){ pvs=mbd.getPropertyValues(); } //遍历容器中全部的后置处理器,执行其中的postProcessProperties方法 for(BeanPostProcessorbp:getBeanPostProcessors()){ if(bpinstanceofInstantiationAwareBeanPostProcessor){ InstantiationAwareBeanPostProcessoribp=(InstantiationAwareBeanPostProcessor) PropertyValuespvsToUse=ibp.postProcessProperties(pvs,bw.getWrappedInstance(),beanName); if(pvsToUse==null){ if(filteredPds==null){ filteredPds=filterPropertyDescriptorsForDependencyCheck(bw,mbd.allowCaching); } pvsToUse=ibp.postProcessPropertyValues(pvs,filteredPds,bw.getWrappedInstance(),beanName); if(pvsToUse==null){ return; } } pvs=pvsToUse; } } } //...省略无关代码 } 可以看看到,在populateBean其内部会循环遍历容器中的后置并判断是否需要属性填充,如果不需要进行属性填充,那么就会直接进行return,反之就会调用容器中的后置处理的postProcessPropertyValues()方法来实现Bean的依赖注入。 进一步,AutowiredAnnotationBeanPostProcessor中postProcessProperties的内部逻辑如下: ?AutowiredAnnotationBeanPostProcessor # postProcessProperties ?publicPropertyValuespostProcessProperties(PropertyValuespvs,Objectbean,StringbeanName){ //1解析出bean中带有@Autowired注解、@Inject和@Value注解的属性和方法 InjectionMetadatametadata=findAutowiringMetadata(beanName,bean.getClass(),pvs); //2遍历标有@Autowire注解的字段,完成反射的注入 metadata.inject(bean,beanName,pvs); return } 其中 findAutowiringMetadata()方法主要对Bean对象中的@Autowired注解进行解析。具体来看, 「对于属性上加了Autowired注解的,经过findAutowiringMetadata的处理后,会将字段解析为AutowiredFieldElement类型」;而如果是方法上加了@Autowired注解,则会解析为AutowiredMethodElement类型。 事实上,无论其扩展于InjectedElement而 metadata.inject()方法则最终会调用InjectedElement类的inject()方法。 具体来看,以@Autowire标注在字段上为例来进行分析。正如之前所说,对于@Autowire标注在字段上的情况,其会将标有@Autowire字段的属性解析为AutowiredFieldElement进而调用其内部的inject方法来完成属性的注入。 其中,AutowiredFieldElement内部inject()的实现逻辑如下: protectedvoidinject(Objectbean,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{ Fieldfield=(Field)this.member; Objectvalue; //判断当前被注入类是否第一次注入,如果不是则从缓存中进行获取 if(this.cached){ value=resolvedCachedArgument(beanName,this.cachedFieldValue); }else{ DependencyDescriptordesc=newDependencyDescriptor(field,this.required); desc.setContainingClass(bean.getClass()); SetStringautowiredBeanNames=newLinkedHashSet(1); Assert.state(beanFactory!=null,"NoBeanFactoryavailable"); TypeConvertertypeConverter=beanFactory.getTypeConverter(); //1通过beanFactory.resolveDependency()方法,来从容器中找到对应的bean信息 value=beanFactory.resolveDependency(desc,beanName,autowiredBeanNames,typeConverter); catch(BeansExceptionex){ thrownewUnsatisfiedDependencyException(null,beanName,newInjectionPoint(field), } //借助反射来完成属性注入 if(value!=null){ ReflectionUtils.makeAccessible(field); field.set(bean,value); } } } 概览inject方法中,不难发现其核心逻辑在于如下两点: ?通过BeanFactory中的resolveDependency找出待注入的value对象利用反射机制,将获取value对象通过反射注入到bean成员中?进一步,对于其中的resolveDependency的逻辑如下。 publicObjectresolveDependency(DependencyDescriptordescriptor,@NullableStringrequestingBeanName, @NullableSetStringautowiredBeanNames,@NullableTypeConvertertypeConverter)throwsBeansException{ //.....省略其他无关逻辑 result=doResolveDependency(descriptor,requestingBeanName,autowiredBeanNames,typeConverter); returnresult; } } 不难发现在resolveDependency内部,其主要依赖doResolveDependency方法来完成bean对象的筛选,其具体逻辑如下: publicObjectdoResolveDependency(DependencyDescriptordescriptor,@NullableStringbeanName, @NullableSetStringautowiredBeanNames,@NullableTypeConvertertypeConverter)throwsBeansException{ InjectionPointpreviousInjectionPoint=ConstructorResolver.setCurrentInjectionPoint(descriptor); try{ ...省略无关代码 //1解析属性,这里筛选出的属性可能是多个。即对于数组、集合、Map类型 ObjectmultipleBeans=resolveMultipleBeans(descriptor,beanName,autowiredBeanNames,typeConverter); if(multipleBeans!=null){ returnmultipleBeans; } //2根据需要注入的类型type,从容器中找到有哪些匹配的Bean MapString,ObjectmatchingBeans=findAutowireCandidates(beanName,type,descriptor); if(matchingBeans.isEmpty()){ if(isRequired(descriptor)){ raiseNoMatchingBeanFound(type,descriptor.getResolvableType(),descriptor); } returnnull; } StringautowiredBeanName; ObjectinstanceCandidate; if(matchingBeans.size()1){ //判断应该使用哪一个bean autowiredBeanName=determineAutowireCandidate(matchingBeans,descriptor); if(autowiredBeanName==null){ if(isRequired(descriptor)||!indicatesMultipleBeans(type)){ returndescriptor.resolveNotUnique(descriptor.getResolvableType(),matchingBeans); } } instanceCandidate=matchingBeans.get(autowiredBeanName); } //...省略无关代码 if(autowiredBeanNames!=null){ autowiredBeanNames.add(autowiredBeanName); } if(instanceCandidateinstanceofClass){ instanceCandidate=descriptor.resolveCandidate(autowiredBeanName,type,this); } Objectresult=instanceCandidate; if(resultinstanceofNullBean){ if(isRequired(descriptor)){ raiseNoMatchingBeanFound(type,descriptor.getResolvableType(),descriptor); } result=null; } if(!ClassUtils.isAssignableValue(type,result)){ thrownewBeanNotOfRequiredTypeException(autowiredBeanName,type,instanceCandidate.getClass()); } returnresult; } finally{ ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } } 对于处逻辑,如果需要注入的属性是普通类型(「即非数组、集合、Map」),那么方法会继续向下执行,会调用下面一行代码,根据属性的类型来查找bean。进而走到。 此处findAutowireCandidates()方法,会从容器中找到对应类型的bean。如果此时无法找到,且Autowired注解的required属性为true那么就会抛出异常。也就是我们常见的:NoSuchBeanDefinitionException。 如果能找到,则会继续执行。首先,matchingBeans这个map的大小可能大于1,因为在spring容器中是有可能找到多个满足类型的Bean信息的。针对这种情况,就需要进行额外的判断了。其逻辑即可能找到多个UserService类型的bean,那么这个时候就需要判断这多个bean中,究竟应该注入哪一个。 因此,其会调用determineAutowireCandidate()方法,判断应该使用哪一个Bean。具体来看, determineAutowireCandidate()其会首先查找标有 **@Primary注解的Bean 「作为候选Bean信息。」 而如果都没有加@Primary注解,那么就会找标有@Priority注解的Bean,进而将优先级高的Bean选中。反之,如果都没有加@Priority,@Primary。那么此时则会先查找类型相匹配的类,例如,如果期待注入一个UserService类型的属性,那么其在注入时则会首先寻找UserService类型的类信息,如果找不到则会根据属性名进行查找。 这也就是我们平常所说的@Autowired注解在注入时的核心逻辑,即其会「先根据类型注入,当碰到多个相同类型时,就会根据属性名注入」。 总结本文首先介绍了@Autowired 注解的作用及使用方式,接着对@Autowired实现原理进行深入剖析。具体来看,@Autowired 自动注入的通过AutowiredAnnotationBeanPostProcessor这一后置处理器来完成的。随后,我们对其调用时机及实现逻辑进行深入剖析。简单来看,@Autowired 自动注入的本质就是反射实现Bean内部实行的赋值。而在筛选候选Bean时所采取的策略则是先根据类型查找,在根据你属性名查找候选Bean。 点击关注公众号,“技术干货”及时达! 阅读原文

上一篇:2025-09-14_LLaSO 横空出世:逻辑智能推出全球首个完全开源语音大模型框架,定义 LSLM 研究新基准 下一篇:2024-02-07_​腾讯研究院2024年新春书单:最值得读的10本书

TAG标签:

15
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价