当前位置: 首页 > news >正文

郑州网站建设网络推广文化礼堂建设情况网站

郑州网站建设网络推广,文化礼堂建设情况网站,广州建设h5网站,福州仓前网站建设前言 问#xff1a;Spring 如何解决循环依赖#xff1f; 答#xff1a;Spring 通过提前曝光机制#xff0c;利用三级缓存解决循环依赖#xff08;这原理还是挺简单的#xff0c;参考#xff1a;三级缓存、图解循环依赖原理#xff09; 再问#xff1a;Spring 通过提前…前言 问Spring 如何解决循环依赖 答Spring 通过提前曝光机制利用三级缓存解决循环依赖这原理还是挺简单的参考三级缓存、图解循环依赖原理 再问Spring 通过提前曝光直接曝光到二级缓存已经可以解决循环依赖问题了为什么一定要三级缓存 再细问如果循环依赖的时候所有类又都需要 Spring AOP 自动代理那 Spring 如何提前曝光曝光的是原始 bean 还是代理后的 bean 这些问题算是 Spring 源码的压轴题了如果这些问题都弄明白恭喜你顺利结业 Spring 源码了。先上图再分析源码 源码分析 进入正题在 Spring 创建 Bean 的核心代码 doGetBean 中在实例化 bean 之前会先尝试从三级缓存获取 bean这也是 Spring 解决循环依赖的开始 (一) 缓存中获取 bean // AbstractBeanFactory.java protected T T doGetBean(final String name, Nullable final ClassT requiredType,Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {final String beanName transformedBeanName(name);Object bean;// 2. 尝试从缓存中获取beanObject sharedInstance getSingleton(beanName);... }getSingleton: protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 从一级缓存获取keybeanName valuebeanObject singletonObject this.singletonObjects.get(beanName);if (singletonObject null isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {// 从二级缓存获取keybeanName valuebeansingletonObject this.earlySingletonObjects.get(beanName);// 是否允许循环引用if (singletonObject null allowEarlyReference) {/*** 三级缓存获取keybeanName valueobjectFactoryobjectFactory中存储getObject()方法用于获取提前曝光的实例** 而为什么不直接将实例缓存到二级缓存而要多此一举将实例先封装到objectFactory中* 主要关键点在getObject()方法并非直接返回实例而是对实例又使用* SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法对bean进行处理** 也就是说当spring中存在该后置处理器所有的单例bean在实例化后都会被进行提前曝光到三级缓存中* 但是并不是所有的bean都存在循环依赖也就是三级缓存到二级缓存的步骤不一定都会被执行有可能曝光后直接创建完成没被提前引用过* 就直接被加入到一级缓存中。因此可以确保只有提前曝光且被引用的bean才会进行该后置处理*/ObjectFactory? singletonFactory this.singletonFactories.get(beanName);if (singletonFactory ! null) {/*** 通过getObject()方法获取bean通过此方法获取到的实例不单单是提前曝光出来的实例* 它还经过了SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法处理过。* 这也正是三级缓存存在的意义可以通过重写该后置处理器对提前曝光的实例在被提前引用时进行一些操作*/singletonObject singletonFactory.getObject();// 将三级缓存生产的bean放入二级缓存中this.earlySingletonObjects.put(beanName, singletonObject);// 删除三级缓存this.singletonFactories.remove(beanName);}}}}return singletonObject;} 三级缓存分别是 singletonObject 一级缓存该缓存key beanName, value bean; 这里的 bean 是已经创建完成的该 bean 经历过实例化-属性填充-初始化以及各类的后置处理。因此一旦需要获取 bean 时我们第一时间就会寻找一级缓存 earlySingletonObjects 二级缓存该缓存key beanName, value bean; 这里跟一级缓存的区别在于该缓存所获取到的 bean 是提前曝光出来的是还没创建完成的。也就是说获取到的 bean 只能确保已经进行了实例化但是属性填充跟初始化肯定还没有做完因此该 bean 还没创建完成仅仅能作为指针提前曝光被其他 bean 所引用 singletonFactories 三级缓存该缓存key beanName, value beanFactory; 在 bean 实例化完之后属性填充以及初始化之前如果允许提前曝光spring 会将实例化后的 bean 提前曝光也就是把该 bean 转换成 beanFactory 并加入到三级缓存。在需要引用提前曝光对象时再通过 singletonFactory.getObject()获取。 这里抛出问题如果我们直接将提前曝光的对象放到二级缓存 earlySingletonObjectsSpring 循环依赖时直接取就可以解决循环依赖了为什么还要三级缓存 singletonFactory 然后再通过 getObject()来获取呢这不是多此一举 (二) 三级缓存的添加 我们回到添加三级缓存添加 SingletonFactory 的地方看看 getObject()到底做了什么操作 this.addSingletonFactory(beanName, () - {return this.getEarlyBeanReference(beanName, mbd, bean); });可以看到在返回 getObject()时多做了一步 getEarlyBeanReference 操作这步操作是 BeanPostProcess 的一种也就是给子类重写的一个后处理器目的是用于被提前引用时进行拓展。即曝光的时候并不调用该后置处理器只有曝光且被提前引用的时候才调用确保了被提前引用这个时机触发。 (三) 提前曝光代理 earlyProxyReferences 因此所有的重点都落到了 getEarlyBeanReference 上getEarlyBeanReference 方法是 SmartInstantiationAwareBeanPostProcessor 所规定的接口。再通过 UML 的类图查看实现类仅有 AbstractAutoProxyCreator 进行了实现。也就是说除了用户在子类重写否则仅有 AbstractAutoProxyCreator 一种情况 // AbstractAutoProxyCreator.java public Object getEarlyBeanReference(Object bean, String beanName) {// 缓存当前bean表示该bean被提前代理了Object cacheKey getCacheKey(bean.getClass(), beanName);this.earlyProxyReferences.put(cacheKey, bean);// 对bean进行提前Spring AOP代理return wrapIfNecessary(bean, beanName, cacheKey); }wrapIfNecessary 是用于 Spring AOP 自动代理的。Spring 将当前 bean 缓存到 earlyProxyReferences 中标识提前曝光的 bean 在被提前引用之前然后进行了 Spring AOP 代理。 但是经过 Spring AOP 代理后的 bean 就已经不再是原来的 bean 了经过代理后的 bean 是一个全新的 bean也就是说代理前后的 2 个 bean 连内存地址都不一样了。这时将再引出新的问题B 提前引用 A 将引用到 A 的代理这是符合常理的但是最原始的 bean A 在 B 完成创建后将继续创建那么 Spring Ioc 最后返回的 Bean 是 Bean A 呢还是经过代理后的 Bean 呢 这个问题我们得回到 Spring AOP 代理Spring AOP 代理时机有 2 个 当自定义了 TargetSource则在 bean 实例化前完成 Spring AOP 代理并且直接发生短路操作返回 bean 正常情况下都是在 bean 初始化后进行 Spring AOP 代理 如果要加上今天说的提前曝光代理getEarlyBeanReference 可以说 3 种 第一种情况就没什么好探究的了直接短路了根本没有后续操作。而我们关心的是第二种情况在 Spring 初始化后置处理器中发生的 Spring AOP 代理 public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result existingBean;for (BeanPostProcessor processor : getBeanPostProcessors()) {// 调用bean初始化后置处理器处理Object current processor.postProcessAfterInitialization(result, beanName);if (current null) {return result;}result current;}return result; }// AbstractAutoProxyCreator.javapublic Object postProcessAfterInitialization(Nullable Object bean, String beanName) {if (bean ! null) {// 获取缓存keyObject cacheKey getCacheKey(bean.getClass(), beanName);// 查看该bean是否被Spring AOP提前代理而缓存的是原始的bean因此如果bean被提前代理过这此处会跳过// 如果bean没有被提前代理过则进入AOP代理if (this.earlyProxyReferences.remove(cacheKey) ! bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}earlyProxyReferences 是不是有点熟悉是的这就是我们刚刚提前曝光并且进行 Spring AOP 提前代理时缓存的原始 bean如果缓存的原始 bean 跟当前的 bean 是一至的那么就不进行 Spring AOP 代理了返回原始的 bean protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Nullable Object[] args)throws BeanCreationException {try {///*** 4. 填充属性* 如果Autowired注解属性则在上方完成解析后在这里完成注入** Autowired* private Inner inner;*/populateBean(beanName, mbd, instanceWrapper);// 5. 初始化exposedObject initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, Initialization of bean failed, ex);}}// 6. 存在提前曝光情况下if (earlySingletonExposure) {// earlySingletonReference二级缓存缓存的是经过提前曝光提前Spring AOP代理的beanObject earlySingletonReference getSingleton(beanName, false);if (earlySingletonReference ! null) {// exposedObject跟bean一样说明初始化操作没用应用Initialization后置处理器(指AOP操作)改变exposedObject// 主要是因为exposedObject如果提前代理过就会跳过Spring AOP代理所以exposedObject没被改变也就等于bean了if (exposedObject bean) {// 将二级缓存中的提前AOP代理的bean赋值给exposedObject并返回exposedObject earlySingletonReference;}// 引用都不相等了也就是现在的bean已经不是当时提前曝光的bean了else if (!this.allowRawInjectionDespiteWrapping hasDependentBean(beanName)) {// dependentBeans也就是B, C, DString[] dependentBeans getDependentBeans(beanName);SetString actualDependentBeans new LinkedHashSet(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}// 被依赖检测异常if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,Bean with name beanName has been injected into other beans [ StringUtils.collectionToCommaDelimitedString(actualDependentBeans) ] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using getBeanNamesOfType with the allowEagerInit flag turned off, for example.);}}}}这个时候我们需要理清一下 3 个变量 **earlySingletonReference**二级缓存缓存的是经过提前曝光提前 AOP 代理的 bean bean这个就是经过了实例化、填充、初始化的 bean exposedObject这个是经过了 AbstractAutoProxyCreator 的 postProcessAfterInitialization 处理过后的 bean但是在其中因为发现当前 bean 已经被 earlyProxyReferences 缓存所以并没有进行 AOP 处理而是直接跳过因此还是跟第 2 点一样的 bean 理清这 3 个变量以后就会发现exposedObject earlySingletonReference; AOP 代理过的 Bean 赋值给了 exposedObject 并返回这时候用户拿到的 bean 就是 AOP 代理过后的 bean 了一切皆大欢喜了。 但是中间还有一个问题提前曝光的 bean 在提前引用时被 Spring AOP 代理了但是此时的 bean 只是经过了实例化的 bean还没有进行Autowire 的注入啊也就是说此时代理的 bean 里面自动注入的属性是空的 (四) 提前 AOP 代理对象的 属性填充、初始化 是的确实在 Spring AOP 提前代理后没有经过属性填充和初始化。那么这个代理又是如何保证依赖属性的注入的呢答案回到 Spring AOP 最早最早讲的 JDK 动态代理上找JDK 动态代理时会将目标对象 target 保存在最后生成的代理proxy中当调用proxy中当调用proxy中当调用proxy 方法时会回调 h.invoke而 h.invoke 又会回调目标对象 target 的原始方法。因此其实在 Spring AOP 动态代理时原始 bean 已经被保存在提前曝光代理中了。而后原始 Bean 继续完成属性填充和初始化操作。因为 AOP 代理$proxy 中保存着 traget 也就是是原始 bean 的引用因此后续原始 bean 的完善也就相当于 Spring AOP 中的 target 的完善这样就保证了 Spring AOP 的属性填充与初始化了 (五) 循环依赖遇上 Spring AOP 图解 为了帮助大家理解这里灵魂画手画张流程图帮助大家理解 首先又 bean Abean B他们循环依赖注入同时 bean A 还需要被 Spring AOP 代理例如事务管理或者日志之类的操作。 原始 bean Abean B 图中用 ab 表示而代理后的 bean A 我们用 aop.a 表示
http://www.dnsts.com.cn/news/103290.html

相关文章:

  • 有没有做奥数题的网站网站建设工作总结6
  • 大兴黄村网站建设公司三网合一网站
  • 长沙 php企业网站系统安宁网站建设熊掌
  • 专业设计网站的公司今天全国生猪价格一览表
  • 网站seo推广计划媒体资源网
  • 如何看网站关键词旅游模板网站
  • 郑州门户网站建设10m带宽做下载网站
  • 网站自定义title项目加盟代理商
  • 精通网站建设 百度云网站制作的要求
  • 职业院校专题建设网站晋州建设规划局网站
  • 网站的二级页面怎么做闵行区seo快速排名优化哪里好
  • windows7建设网站网页设计模板html代码怎么学
  • 网站的速度诊断怎么做网络推广优化招聘
  • 网站飘落怎么做礼品兑换网站怎么做
  • 有了域名建设网站韩国优秀网站设计
  • 子商务网站建设的一般流程软文标题和内容
  • 个人建设网站流程图公司在百度做网站找谁
  • 网站备案所需材料专业宣传片制作拍摄公司
  • 免费手机建网站平台关于seo关键词选择有哪些方法
  • 网站设计说明书800字店铺设计用什么软件
  • 北京模板网站建设全包Wordpress 标签无用
  • 网站建设的技术wordpress随机文章代码
  • 加拿大pc网站搭建好的网站设计作品
  • 做网站销售有前景wordpress搜索不了中文linux
  • 有没有做英语题的网站中国营销策划第一人
  • 网站开发项目的需求分析建筑服务网站企业
  • 网站有冒号怎么打开模版之家
  • 做免费嗳暧视频网站视频生成链接网站
  • 可以在手机建网站的家具东莞网站建设技术支持
  • 中国建设银行网上银行官方网站wordpress 煎蛋网插件