欢迎来到淘文阁 - 分享文档赚钱的网站! | 帮助中心 好文档才是您的得力助手!
淘文阁 - 分享文档赚钱的网站
全部分类
  • 研究报告>
  • 管理文献>
  • 标准材料>
  • 技术资料>
  • 教育专区>
  • 应用文书>
  • 生活休闲>
  • 考试试题>
  • pptx模板>
  • 工商注册>
  • 期刊短文>
  • 图片设计>
  • ImageVerifierCode 换一换

    Spring源码最难问题:当Spring AOP遇上循环依赖.docx

    • 资源ID:86404980       资源大小:70.27KB        全文页数:13页
    • 资源格式: DOCX        下载积分:15金币
    快捷下载 游客一键下载
    会员登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录   QQ登录  
    二维码
    微信扫一扫登录
    下载资源需要15金币
    邮箱/手机:
    温馨提示:
    快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
    如填写123,账号就是123,密码也是123。
    支付方式: 支付宝    微信支付   
    验证码:   换一换

     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    Spring源码最难问题:当Spring AOP遇上循环依赖.docx

    Spring源码最难问题:当 Spring AOP 遇上循环依赖 前a问:Spring如何解决循环依赖?答:Spring通过提前曝光机制,利用三级缓存解决循环 依赖。再问:Spring通过提前曝光,直接曝光到二级缓存已经 可以解决循环依赖问题了,为什么一定要三级缓存?再细问:如果循环依赖的时候,所有类又都需要Spring AOP自动代理,那Spring如何提前曝光?曝光的是原始 bean还是代理后的bean?这些问题算是Spring源码的压轴题了,如果这些问题都 弄明白,恭喜你顺利结业Spring源码了。就单单对Spring 这一块的理解,不夸张的说可以到达阿里水准了源码分析/小* 4.填充属性*如果Autowired注解属性,那么在上方完成解析后,在这里完成注入Autowiredrivate Inner inner;populateBean(beanName, mbd, instanceWrapper);/5.初始化exposedObject = initializeBean(beanName, exposedObject, mbd);|ch (Throwable ex) il (ex nstnnceof BeanCreationException && beanName.equals(BeanCr|ua(i()iil-.ccplion > c i.L'clI-k'uii.Wimci i)throw (BeanCreationException) ex;|else throw no BeanCreationException(mbd.getResourceDescription。,beanName, "Initialization of bean failed", ex);/6.存在提前曝光情况下Object carlySinglctonRcfcrcncc 二 gctSinglcton(bcanNamc, | (c;iSin9h、l()nRclccni'c I 7| i 八pi)、cd( )h|ci l hc;in i 1 腐螫够,:| cxp()、cd()bjccl C”L SinglclonR匕k| else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(| Siring" dependentBeans 二 ge【DependentBeans(beanNanie | SetString> actualDependenlBeans = nc LinkedHashSet<>(dependent 卜 m -, m 隙; | (Siring dependenlBeiin : dcpendenlBenn、)| if (!removeSingletonIfCreatedForTypeCheckOnIy(dependentB6an)actual DependentBeans.add(dependentBean);/被依赖检测异|(匕 c I u ” D c p c n d c n B c a n、. i s IS m p l y ( )J,卷懿|throw new B canC u rrc n U y 1 nCcat iu n Ex ccpt i on (bean N a me| "Bean with name + beanName + has been injected into other be| StringUtils.coHectionToCommaDelimitedString(actualDependentBe|" in its raw version as part of a circular reference, but has eventual"wrapped. This means that said other beans do not use the final versi“bean. This is often the result of over-eager type nialchintj - consideetBeanNamesOfType' with the 'allowEagerlnit' flag turned off, fo|这个时候我们需要理清一下3个变量earlySingletonReference:二级缓存,缓存的是经过提前曝光提前AOP代理的beanbean:这个就是经过了实例化、填充、初始化的beanexposedObject : 这个是经过了 AbstractAutoProxyCreator 的 postProcessAfterlnitialization 处理 过后的bean,但是在其中因为发现当前bean已经被 earlyProxyReferences缓存,所以并没有进行AOP处理, 而是直接跳过,因此还是跟第2点一样的bean 理清这3个变量以后,就会发现,exposedObject = earlySingletonReference;AOP代理过的Bean赋值给了 exposedObject并返回,这时 候用户拿到的bean就是AOP代理过后的bean 了,一切 皆大欢喜了。但是中间还有一个问题!提前曝光的bean在提前引用时 被Spring AOP代理了,但是止匕时的bean只是经过了实例 化的bean,还没有进行Autowire的注入啊!也就是说此 时代理的bean里面自动注入的属性是空的!另外,搜索 公众号Linux就该这样学后台回复“猴子”,获取一份惊喜 礼包。(四)提前AOP代理对象的属性填充、初始化是的,确实在Spring AOP提前代理后没有经过属性填充 和初始化。那么这个代理又是如何保证依赖属性的注入的呢?答案回到Spring AOP最早最早讲的JDK动态代理 上找,JDK动态代理时,会将目标对象target保存在最 后生成的代理$proxy中,当调用$proxy方法时会回调 h.invoke,而h.invoke又会回调目标对象target的原始方 法。因此,其实在Spring AOP动态代理时,原始bean已经被 保存在提前曝光代理中了。而后原始Bean继续完成属性 填充和初始化操作。因为AOP代理$proxy中保存着traget 也就是是原始bean的引用,因此后续原始bean的完善, 也就相当于Spring AOP中的target的完善,这样就保证 了 Spring AOP的属性填充与初始化了!(五)循环依赖遇上Spring AOP图解为了帮助大家理解,这里灵魂画手画张流程图帮助大家 理解首先又bean A, bean B,他们循环依赖注入,同时bean A 还需要被Spring AOP代理,例如事务管理或者日志之类 的操作。原始bean A, bean B图中用a, b表示,而代理 后的bean A我们用aop.a表不。1,袁道从各圾播存中我取beangetSlnglGton(Ba")7翟试从各圾媚存中技取beangetSingletonC'b'')13.言词2.检冽a是否在创立状态crtlnCreate.contains("a")8.检测b是否在创立状态crtlnCreate.containsC'b")此时在第的a的工 aFac3.创立前,先将a标记为创立中crtlnCreate.add( "a")9创立前,先将b标记为创立中crtlnCreate.addCb")4.实例化bean = createBeanlnstanceC'a")10.实例化bean = createBeanlnstanceCb")5.将bean保存到三级缓存中addSinqletonFactoryl 带 a 的工厂);11.将bean保存到三级缓存中addSingtetonFactory(带 b 的工厂);b填充的属性是aop-a而非a6.填充属性发现需变引用BgotBoan("b")12.塌充属性发现需要引用AgotBoan(-a")19.初始化17.初始化21.创立完成,标识a不在创立中 crtlnCreate.removeC'a")怕.创立完成,标识b不在创立中 crtlnCreate.remove("b")AB+ b:B+a : AcurrentlylnCreation:正在创立的bean集合;下方简称crtl, ;昂+getA什earlySingletonObjects:二级缓存,缓存提前曝光的实例;下工> setB(B b);4setA(Aa);singletonFactories:三短缓存,缓存提前曝光的工厂,用于生产二级名getBean("b")getBean("b")getBeanCa")看完也算是Spring的顺利结业了 !进入正题,在Spring创立Bean的核心代码doGetBean中, 在实例化bean之前,会先尝试从三级缓存获取bean,这 也是Spring解决循环依赖的开始(一)缓存中获取bean/ AbstractBeanFactory.iava<T> T doGetBean。inal String name, Nullable lina ClassT> rNullable inal Object args, boolean typeCheckOnly) throws BeansExString beanName = transfoimedBeanName(name);Object bean;/ 2.尝试从缓存中获取beanObject sharcdlnstance = getSingleton(beanName);rotcctcd Object getSingleton(String beanName, boolean allowEarlyRefere nee) 从一级缓存获取,key=beanName value=beanObject singletonObject = this.singletonObiects.get(beanName);I (singletonObject = nu && isSingletonCurrentlylnCreation(beanName)singlclon Object 二.carl ySinglctonObjccts.gct( bean Name );BMHBBMH(singlclonObjccl 二二 && zillov£alyRcfcrcncc) * 三级缓存获取,key-beanName value=objectFactory, objectFactory 中* SmartlnstantiationAwareBeanPostProcessor 的 getEarlyBeanReferenceObjectFactoryv?> singletonFactory = Uiis.singletonFactories.get(beanNam|3S(singletonFactory != null) * 它 还经过 了 SmartlnstantiationAwareBeanPostProcessor 的也正)置处 匚提前singletonObjecl = singlelonFaclory.getObjecMthis.earlySingletonObjects.put(beanName, singletonObject);.singlulonl iicl()rius.rciiio ci 鼠二;uncrcluri singletonObject;三级缓存分别是:singletonObject : 一级缓存,该缓存 key = beanName, value = bean;这里的bean是已经创立完成的, 该bean经历过实例化,属性填充,初始化以及各类的 后置处理。因此,一旦需要获取bean时,我们第一时 间就会寻找一级缓存earlySingletonObjects:二级缓存,该缓存 key = beanName, value = bean;这里跟一b级缓存的区别在于,该 缓存所获取到的bean是提前曝光出来的,是还没创立 完成的。也就是说获取到的bean只能确保已经进行了 实例化,但是属性填充跟初始化肯定还没有做完,因 此该bean还没创立完成,仅仅能作为指针提前曝光, 被其他bean所引用singletonFactories : 三级缓存,该缓存 key = beanName, value = beanFactory;在 bean 实例化完之后,属 性填充以及初始化之前,如果允许提前曝光,spring 会将实例化后的bean提前曝光,也就是把该bean转 换成beanFactory并加入到三级缓存。在需要引用提前 曝光对象时再通过singletonFactory.getObject()获取。这里抛出问题,如果我们直接将提前曝光的对象放到二 级缓存earlySingletonObjects, Spring循环依赖时直接取就 可以解决循环依赖了,为什么还要三级缓存 singletonFactory然后再通过getObject()来获取呢?这不是 多此一举?(二)三级缓存的添加我们回到添加三级缓存,添加SingletonFaclory的地方,看 看gelObjectO到底做了什么操作reti” n this.getEarlyBeanRefcrence(beanName, mbd, bean);tn可以看到在返回getObject()时,多做了 一步 getEarlyBeanReference 操作,这步操作是 BeanPostProcess 的 一种,也就是给子类重写的一个后处理器,目的是用于 被提前引用时进行拓展。另外,搜索公众号后端架构师 后台回复“物联网”,获取一份惊喜礼包。即:曝光的时候并不调用该后置处理器,只有曝光,且 被提前引用的时候才调用,确保了被提前引用这个时机 触发。(三)提前曝光代理 earlyProxyReferences因此所有的重点都落到了 getEarlyBeanReference上, getEarlyBeanReference方法是Smartinstantiation A wareBeanPostProcessor 所规定的接口。再 通过 UML的类图查看实现类,仅有 Abstract AutoProxyCreator进行了实现。也就是说,除了用户 在子类重写,否那么仅有AbstractAutoProxyCreator一种情况/ AbstractAutoProxyCreator.iavpublic Object getEarlyBeanReference(Object bean, Siring beanNamc) Object cacheKcy = gctCachcKey(bcan.getClass(), beanName);. cnrl'P()yRckrcncc、.pul(cacheKc” been 1憾w rap! I'Neccssai yi bean. bccuiXanic, cachcKc)wrapIfNecessary 是用于 Spring AOP 自动代理的。Spring 将 当前bean缓存到earlyProxyReferences中标识提前曝光的 bean在被提前引用之前,然后进行了 Spring AOP代理。牛逼啊!接私活必备的N个开源工程!赶快收藏吧但是经过Spring AOP代理后的bean就已经不再是原来的 bean 了,经过代理后的bean是一个全新的bean,也就是 说代理前后的2个bean连内存地址都不一样了。这时将再引出新的问题:B提前引用A将引用到A的代 理,这是符合常理的,但是最原始的bean A在B完成创 建后将继续创立,那么Spring loc最后返回的Bean是 Bean A呢还是经过代理后的Bean呢?这个问题我们得回到Spring AOP代理,Spring AOP代理 时机有2个:当自定义了 TargetSource,那么在bean实例化前完 成Spring AOP代理并且直接发生短路操作,返回bean正常情况下,都是在bean初始化后进行Spring AOP代理如果要加上今天说的提前曝光代理,getEarlyBeanReference 可以说3种第一种情况就没什么好探究的了,直接短路了,根本没 有后续操作。而我们关心的是第二种情况,在Spring初 始化后置处理器中发生的Spring AOP代理Ipuhlic Object applyBeanPostProcessorsAfterInitialization(Objcct existingRin.、I11;i八;in;一、-111、川()h |、门 i c、u 11 八 71 n 口 卜;c n i:for (BcanPostProccssor processor : gclBcanPoslProccssorsQ) Object current = processor.postProcessAfterlnitialization(result, beanName);result = current;!rctu i n result;/ AbstractAutoProxyCreator.iavapublic Object postProcessAfterInitialization(Nullablc Object bean, Strin|Objccl cachcKcy = gclCachcKcy(bcan*ctClassQ, bcanNanic)、,|( .carlyProxyRcfcrcnccs.rcmo'c(cachcKcy) !二 bean) 1 rapl iNcccssarx( lum. bean a me. cn C v):retn rn bean;earlyProxyReferences是不是有点熟悉,是的,这就是我们 刚刚提前曝光并且进行Spring AOP提前代理时缓存的原 始bean,如果缓存的原始bean跟当前的bean是一至的, 那么就不进行Spring AOP代理了!返回原始的bean。扩 展:接私活儿protected Object doCreateBean(final String bcanName, fin;i RootBeanDefinilion nihd. («NuHnhlc()卜心1 |throws BeanCreationException

    注意事项

    本文(Spring源码最难问题:当Spring AOP遇上循环依赖.docx)为本站会员(太**)主动上传,淘文阁 - 分享文档赚钱的网站仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知淘文阁 - 分享文档赚钱的网站(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    关于淘文阁 - 版权申诉 - 用户使用规则 - 积分规则 - 联系我们

    本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

    工信部备案号:黑ICP备15003705号 © 2020-2023 www.taowenge.com 淘文阁 

    收起
    展开