什么神仙,这Spring注入对象处理过程也太细了,收藏了
1、自动装配与@Autowired这里首先做一个区分 , 因为在之前的很长一段时间内 , 我都错误的以为@Autowired就是自动装配 。 这也就引发了我一直错误的任务Spring的自动装配首先是byType然后是byName的 。 通过这段时间对于源码的阅读 , 我才意识到这个错误 。
当涉及到自动装配Bean的依赖关系时 , Spring提供了4种自动装配策略 。
publicinterfaceAutowireCapableBeanFactory{//无需自动装配intAUTOWIRE_NO=0;//按名称自动装配bean属性intAUTOWIRE_BY_NAME=1;//按类型自动装配bean属性intAUTOWIRE_BY_TYPE=2;//按构造器自动装配intAUTOWIRE_CONSTRUCTOR=3;//过时方法 , Spring3.0之后不再支持@DeprecatedintAUTOWIRE_AUTODETECT=4;...}1.1自动装配
在xml中定义Bean的时候 , 可以通过如下的方式指定自动装配的类型 。
如果使用了根据类型来自动装配 , 那么在IOC容器中只能有一个这样的类型 , 否则就会报错!
1.2使用注解来实现自动装配
@Autowired注解 , 它可以对类成员变量、方法及构造函数进行标注 , 完成自动装配的工作 。 Spring是通过@Autowired来实现自动装配的 。 当然 , Spring还支持其他的方式来实现自动装配 , 如:「JSR-330的@Inject注解」、「JSR-250的@Resource注解」 。
通过注解的方式来自动装配Bean的属性 , 它允许更细粒度的自动装配 , 我们可以选择性的标注某一个属性来对其应用自动装配 。
2、依赖注入在这篇文章中 , 我将详细的分析 , 在一个对象中通过@Autowired注入或@Resource注入属性的处理过程 。 这里我还是采取使用情形 , 然后画出简要流程图 , 最后再是源码分析的方式来介绍本文所要涉及的知识点 。
2.1日常开发中注入对象的方式
「情形一」:通过@Autowired注解对象的方式
@ServicepublicclassDemoServiceTwo{@AutowiredDemoServiceThreedemoServiceThree;}「情形二」:通过@Autowired注解构造器的方式
@ServicepublicclassDemoServiceTwo{DemoServiceOnedemoServiceOne;@AutowiredpublicDemoServiceTwo(DemoServiceOnedemoServiceOne){this.demoServiceOne=demoServiceOne;}}「情形三」:通过@Resource注解对象的方式
@ServicepublicclassDemoServiceTwo{@ResourceDemoServiceOnedemoServiceOne;}「情形四」:通过@Autowired注解方法的方式
@ServicepublicclassDemoServiceTwo{DemoServiceOnedemoServiceOne;@Autowiredpublicvoidprepare(DemoServiceOnedemoServiceOne){this.demoServiceOne=demoServiceOne;}}上述的四种方式是我们在日常开发中经常用到的注入对象的方式 。 这四种方式 , 在Spring对应不同的处理逻辑 。
2.2对象之间依赖关系处理流程
两种注解的处理方式都是通过后置处理器来完成处理的 , getBeanPostProcessors()在我们不做任何扩展的情况下 , Spring中只有五个 。 如有忘记请查看:「容器初始化先发五虎」;对于@Resource的处理是通过CommonAnnotationBeanPostProcessor来完成的 。 对于@Autowired的处理是通过AutowiredAnnotationBeanPostProcessor来处理的 。 对于@Autowired注解构造器的方式 , 获取到被注解元素为null则直接返回 。 完成populateBean()的过程 。 对于剩下的情形 , 处理思路一致 , 都是先获取到被注入的对象 , 然后将维护对象属性之间的关系 。 重点突出一下getBean()这里还是我们熟悉的getBean()...field.set()维护对象之间的依赖关系 。 3、源码分析源码分析首当其冲的就是方法入口 , 在对象的包装BeanWrapper创建完成之后 , populateBean()来处理对象之间的依赖关系:
protectedvoidpopulateBean(StringbeanName,RootBeanDefinitionmbd,@NullableBeanWrapperbw){if(bw==null){if(mbd.hasPropertyValues()){thrownewBeanCreationException(mbd.getResourceDescription(),beanName,"Cannotapplypropertyvaluestonullinstance");}else{//Skippropertypopulationphasefornullinstance.//没有任何属性需要填充return;}}if(!mbd.isSynthetic()&&hasInstantiationAwareBeanPostProcessors()){for(BeanPostProcessorbp:getBeanPostProcessors()){if(bpinstanceofInstantiationAwareBeanPostProcessor){InstantiationAwareBeanPostProcessoribp=(InstantiationAwareBeanPostProcessor)bp;/***InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation()方法的应用*可以控制程序是否进行属性填充*/if(!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(),beanName)){return;}}}}/***取得BeanDefinition中设置的Property值 , *这些property来自对BeanDefinition的解析 , *具体的过程可以参看对载入个解析BeanDefinition的分析*这里是Spring内部设置的属性值 , 一般不会设置*/PropertyValuespvs=(mbd.hasPropertyValues()?mbd.getPropertyValues():null);/***处理自动装配 , xml的方式可能会有配置自动装配类型的情况*或者通过setAutowireMode()的方式设置自动装配的模式*/intresolvedAutowireMode=mbd.getResolvedAutowireMode();//Spring默认既不是byType也不是byName,默认是null//这里之所以做这个判断是因为某种特殊的场景下 , 会修改到自动注入的模型 , 所以需要做判断if(resolvedAutowireMode==AUTOWIRE_BY_NAME||resolvedAutowireMode==AUTOWIRE_BY_TYPE){MutablePropertyValuesnewPvs=newMutablePropertyValues(pvs);/***byName处理*通过反射从当前Bean中得到需要注入的属性名 , *然后使用这个属性名向容器申请与之同名的Bean , *这样实际又触发了另一个Bean生成和依赖注入的过程*/if(resolvedAutowireMode==AUTOWIRE_BY_NAME){autowireByName(beanName,mbd,bw,newPvs);}//byType处理if(resolvedAutowireMode==AUTOWIRE_BY_TYPE){autowireByType(beanName,mbd,bw,newPvs);}pvs=newPvs;}//后置处理器已经初始化booleanhasInstAwareBpps=hasInstantiationAwareBeanPostProcessors();//需要检查依赖booleanneedsDepCheck=(mbd.getDependencyCheck()!=AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);PropertyDescriptor[]filteredPds=null;if(hasInstAwareBpps){if(pvs==null){//与构造方法的处理一样 , 对象有但对象里面的属性没有pvs=mbd.getPropertyValues();}for(BeanPostProcessorbp:getBeanPostProcessors()){if(bpinstanceofInstantiationAwareBeanPostProcessor){InstantiationAwareBeanPostProcessoribp=(InstantiationAwareBeanPostProcessor)bp;//通过后置处理器来完成处理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;}}}if(needsDepCheck){if(filteredPds==null){filteredPds=filterPropertyDescriptorsForDependencyCheck(bw,mbd.allowCaching);}checkDependencies(beanName,mbd,filteredPds,pvs);}if(pvs!=null){//对属性进行注入applyPropertyValues(beanName,mbd,bw,pvs);}}通过源码实现可以看出「自动装配」与「注解注入」的处理是有差别的 。 其中自动装配时通过属性判断来完成 。 注解注入是通过「后置处理器」来完成的 。
不同的后置处理器的postProcessProperties()方法对应的是不同的处理逻辑 。
4、@Autowired注解属性4.1处理过程
首先 , 通过findAutowiringMetadata()方法获取被注入的元数据 。 以上述:「情形一」 , 为例:
对于@Autowired注解的其他使用方式 , 最终都会调用element.inject(target,beanName,pvs);
接下来将分析resolveDependency()方法 , 通过该方法可以发现 , 最终调用的是doResolveDependency() 。 看到了doXXXX()的方法 , 就又到了Spring惯用的套路了 , 这里这方法就是真正做事的方式 。 下面就来看看在doResolveDependency()中做了什么事情...
publicObjectdoResolveDependency(DependencyDescriptordescriptor,@NullableStringbeanName,@NullableSetautowiredBeanNames,@NullableTypeConvertertypeConverter)throwsBeansException{InjectionPointpreviousInjectionPoint=ConstructorResolver.setCurrentInjectionPoint(descriptor);try{Objectshortcut=descriptor.resolveShortcut(this);if(shortcut!=null){returnshortcut;}/***根据类型获取 , @Autowired默认根据type*/Class>type=descriptor.getDependencyType();/***支持@value注解*/Objectvalue=https://pcff.toutiao.jxnews.com.cn/p/20200906/getAutowireCandidateResolver().getSuggestedValue(descriptor);if(value!=null){if(valueinstanceofString){StringstrVal=resolveEmbeddedValue((String)value);BeanDefinitionbd=(beanName!=null&&containsBean(beanName)?getMergedBeanDefinition(beanName):null);value=evaluateBeanDefinitionString(strVal,bd);}TypeConverterconverter=(typeConverter!=null?typeConverter:getTypeConverter());try{/**通过转换器将Bean的值转换为对应的type类型*/returnconverter.convertIfNecessary(value,type,descriptor.getTypeDescriptor());}catch(UnsupportedOperationExceptionex){//AcustomTypeConverterwhichdoesnotsupportTypeDescriptorresolution...return(descriptor.getField()!=null?converter.convertIfNecessary(value,type,descriptor.getField()):converter.convertIfNecessary(value,type,descriptor.getMethodParameter()));}}ObjectmultipleBeans=resolveMultipleBeans(descriptor,beanName,autowiredBeanNames,typeConverter);if(multipleBeans!=null){returnmultipleBeans;}/***根据属性类型找到beanFactory中所有类型匹配的Bean*返回值的结构为:*key:匹配的BeanName;*value:beanName对应的实例化后的bean通过getBean(beanName)返回*/MapmatchingBeans=findAutowireCandidates(beanName,type,descriptor);if(matchingBeans.isEmpty()){/***如果autowire的require属性为true*找到的匹配项为空则抛出异常*/if(isRequired(descriptor)){raiseNoMatchingBeanFound(type,descriptor.getResolvableType(),descriptor);}returnnull;}StringautowiredBeanName;ObjectinstanceCandidate;//根据类型匹配到的数量大于1个if(matchingBeans.size()>1){//确定自动注入的beanNameautowiredBeanName=determineAutowireCandidate(matchingBeans,descriptor);if(autowiredBeanName==null){if(isRequired(descriptor)||!indicatesMultipleBeans(type)){returndescriptor.resolveNotUnique(descriptor.getResolvableType(),matchingBeans);}else{returnnull;}}instanceCandidate=matchingBeans.get(autowiredBeanName);}else{//Wehaveexactlyonematch.Map.Entryentry=matchingBeans.entrySet().iterator().next();autowiredBeanName=entry.getKey();instanceCandidate=entry.getValue();}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);}}分析上述代码 , 可以发现其中主要做了如下的几件事:
①:获取注入对象的类型;
②:解析过程中对@value注解支持;
③:通过findAutowireCandidates()方法 , 根据属性类型找到BeanFactory中所有类型匹配的Bean 。 存放在Map中返回 。 在该方法中 , 会根据给定的类型获取所有Bean的名称作为Map中的key 。
对类型匹配的Bean做相应的判断 , 如果大于1个 , 则通过determineAutowireCandidate()方法来确定注入Bean的名称 。 首先根据@Primary注解的对象来确定 , 如果有则返回;然后再通过@Priority注解的对象 , 如果有则返回 。 如果等于1个 , 在返回Map中的key就是beanName;通过上述的描述发现@Autowired注解属性的方式先通过byType的方式获取对应类型的对象;当对应类型的对象大于1个时 , 通过byName的方式来确定 。
④:最后descriptor.resolveCandidate(autowiredBeanName,type,this);通过beanFactory.getBean(beanName);获取注入的对象 。
4.2对象之间依赖关系的维护
在通过beanFactory.resolveDependency()方法获得依赖的对象之后 , 通过registerDependentBeans()方法来维护对象之间的依赖关系 。
/**指定的bean与目前已经注册的依赖这个指定的bean的所有依赖关系的缓存(我依赖的)*/privatefinalMap>dependentBeanMap=newConcurrentHashMap<>(64);/**指定bean与目前已经注册的创建这个bean所需依赖的所有bean的依赖关系的缓存(依赖我的)*/privatefinalMap>dependenciesForBeanMap=newConcurrentHashMap<>(64);复制代码在上述的方法中 , 就是通过上述两个Map维护了对象间依赖与被依赖的关系 , 详细看下图
5、@Autowired注解构造器的处理方式前面介绍过 , 通过@Autowired注解构造器的方式 , 在populateBean()方法中 , 通过后置处理器来处理时 , 获取到的被注入的元素为空 , 因此直接返回 。 也就是说这里并没有维护对象之间的依赖关系 。 但是对象和属性之间的依赖关系 , 在通过构造器实例化对象的时候已经依赖好了 。 我自己的理解就是java对象和对象属性之间的关系已经有了 。
6、总结本文主要介绍了Spring中对象之间依赖关系的处理流程 。 通过流程图的方式 , 粗略的看了一下@Resource和@Autowired注解处理的过程 。
本文详细介绍了@Autowired注解属性的处理过程、java对象与属性关系的维护以及Spring对象之间的依赖关系的维护 。
简单介绍了@Autowired注解构造器的处理构成 。
关于@Resource注解与@Autowired注解方法的处理过程 , 后面有机会再详细分析 。
【什么神仙,这Spring注入对象处理过程也太细了,收藏了】作者:零星链接:来源:掘金
推荐阅读
- 养老金|2021年上半年办理退休,养老金核算的这些知识要把握
- 喝酒|长期喝酒者,早起后,若有这5个表现,你得考虑戒酒保肝了!
- 兔子|兔兔这么可爱,为什么要吃屎?
- 百度|AI公司百度能给港交所带来什么?
- 这款抹茶麻薯软欧,简单好做,满满坚果馅,好吃还不腻
- 春天湿气重,多喝这碗糖水,祛湿清热又甘甜,我隔两天喝一次
- 盐酥烧饼这做法,皮酥里软吃着香,不醒面照样层层酥脆,省时间
- 这早餐我从3岁开始吃,三十多年了,从没吃腻过,晶莹剔透很好吃
- 剩米饭别再炒了,试试这样做,比蛋炒饭好吃一百倍
- 这款蛋黄肉松面包,柔软又香甜,吃过的人都说好!
