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

做网站那家好重庆seo霸屏

做网站那家好,重庆seo霸屏,网站后台管理系统操作,深圳做app开发场景复现 1、首先项目需要打开spring的异步开关,在application主类上加EnableAsync 2、创建一个包含了Async方法的异步类MessageService: Service public class MessageService {Resource private TaskService taskService; Async public void…

场景复现

1、首先项目需要打开spring的异步开关,在application主类上加@EnableAsync
2、创建一个包含了@Async方法的异步类MessageService:

@Service
public class MessageService {@Resource    private TaskService taskService;   @Async    public void send(){taskService.shit();    }
}

3、创建另一个正常类TaskService,与异步类形成循环引用的关系(注意MessageService和TaskService在同一个包内,并且order为默认,因此会先扫描MessageService再扫描TaskService):

@Service
public class TaskService {@Resource    private MessageService messageService;  public void shit(){System.out.println();    }
}

4、启动springboot项目成功报错

问题出现的原因

在分析原因之前,我们需要提前知道两个重要的点:

  1. spring的aop代理(包括@Transactional 事务代理),都是在AbstractAutowireCapableBeanFactory的populateBean方法后的initializeBean当中的applyBeanPostProcessorsAfterInitialization方法里,通过特定的后置处理器里创建代理对象(如果用@Autowired则是AnnotationAwareAspectJAutoProxyCreator)
  2. 然而1.当中描述的代理过程,是在这个类不涉及到循环引用的情况下才会执行,也就是说满足百分之90的情况,而循环引用的情况会做特殊的处理,即提前创建代理对象。

举个例子: T类是个包含了@Transactional方法的类,属于需要被代理的对象,并且通过@Resource(或者@Autowired)的方式依赖了A ,A类中也以同样的方式注入了T,并且T类先于A类开始实例化过程,那么简单的实例化流程就是:

  • T的BeanDefinition被spring拿到后,根据构造器实例化一个T对象(原始对象而非代理对象),并包装成objectFactory放入singletonFactories(三级缓存)中 然后执行populateBean方法开始注入属性的流程,其中会利用CommonAnnotationBeanPostProcessor(@Resource用这个后置处理器,@Autowired用 AutowiredAnnotationBeanPostProcessor)执行T的属性注入步骤,遍历T中所依赖的属性
  • 发现T依赖了A,会先到beanFactory的一至三级缓存中,通过A的beanName查询A对象,如果没找到,即A还没有被实例化过,那么会将A作为实例化的目标,重复a.步骤:将A实例化后的对象包装成objectFactory放入singletonFactories,接着对A执行populateBean来注入属性
  • 遍历A的属性,发现A依赖了T,然后尝试去beanFactory中获取T的实例,发现三级缓存中存在T的objectFactory,因此执行objectFactory.getObject方法企图获取T的实例。然而这个objectFactory并非是简单把对象返回出去,而是在当初包装的时候,就将AbstractAutowireCapableBeanFactory的getEarlyBeanReference方法写入getObject当中
  • 在getEarlyBeanReference方法里,会遍历所有SmartInstantiationAwareBeanPostProcessor的子类型的后置处理器,执行对应的getEarlyBeanReference方法,此时会将第1.点提到的代理过程提前,即通过 AnnotationAwareAspectJAutoProxyCreator(SmartInstantiationAwareBeanPostProcessor的子类)来创建一个代理对象,并放入二级缓存earlySingletonObjects当中,然后将这个代理对象通过field.set的形式(默认形式)注入到A,至此就完成了普通aop对象的循环引用处理

出现本文标题中循环引用异常的原因分析

包含了@Async 方法的类与@Transactional的类相似,也会被替换成一个新的代理类,但是与普通aop不同的是,@Async不会在 getEarlyBeanReference 阶段执行创建代理的逻辑(这么做的原因暂时没仔细分析),而是被延迟到了initializeBean步骤当中(即1.提到的90%的代理情况),这样一来就会导致TaskService注入的并不是最终创建完成的MessageService的代理对象,很明显这样的结果是不合理的,而在代码层面,spring的AbstractAutowireCapableBeanFactory当中,在initializeBean和将bean放入一级缓存之间,有这么一段容易被忽视的代码,用于把控最终的循环引用结果正确性:

//是否允许提前暴露,可以理解为是否允许循环引用
if (earlySingletonExposure) {//遍历一到三级缓存,拿到的beanObject earlySingletonReference = getSingleton(beanName, false);//如果缓存中的对象不为空if (earlySingletonReference != null) {//exposedObject是执行了initializeBean之后的对象,bean是通过构造器创建的原始对象//如果两者相等,则将exposedObject设置为缓存中的对象if (exposedObject == bean) {exposedObject = earlySingletonReference;}   //如果两者不是同一个对象,并且不允许直接注入原生对象(默认false),且当前beanName有被其他的bean所依赖else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {//则获取所有依赖了该beanName的对象String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {//如果这个对象已经处于一级缓存当中,则添加到actualDependentBeans,即依赖该对象的bean是一个走完了整个流程,不会再有机会回炉重做的beanif (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}//最后判断actualDependentBeans是否为空,不为空就抛循环引用的异常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.");}}}
}
  • 我们结合这段代码来分析@Async 循环引用场景:
    • 先看第4行,首先这个时候肯定还没进入一级缓存,而我们知道@Async在 getEarlyBeanReference 中并没有执行代理,因此第4行获取到的 earlySingletonReference 是messageService的原始对象
    • 进入第9行,判断exposedObject == bean,由于@Async的代理过程发生在initializeBean中, 因此exposedObject是代理对象,而bean是通过构造器直接实例化的原始对象,因此肯定不相等
    • 进入第12行,allowRawInjectionDespiteWrapping默认为false,而messageService是被TaskService所引用的,因此 hasDependentBean(beanName)为true ,会进入14行代码块
    • 重点是判断18行的!removeSingletonIfCreatedForTypeCheckOnly(dependentBean),该方法代码为:
protected boolean removeSingletonIfCreatedForTypeCheckOnly(String beanName) {
//如果不是已经完全创建好的bean,就返回true,否则返回falseif (!this.alreadyCreated.contains(beanName)) {removeSingleton(beanName);return true;}else {return false;}
}

这里就要回到场景复现时提到的:

3、注意MessageService和TaskService在同一个包内,并且order为默认,因此会先扫描MessageService再扫描TaskService。

    • 由于messageService先被扫描,因此会在messageService的populateBean当中,执行TaskService的实例化过程,而TaskService此时并不知道messageService是一个需要代理的类,因此将一个未代理的messageService注入之后,心安理得地执行了initializeBean以及后续的初始化操作,然后标记为成功创建并装入一级缓存。
    • 也就是说,此时spring判断TaskService是一个已经完全实例化并初始化完成的对象。因此removeSingletonIfCreatedForTypeCheckOnly方法会返回false,则18行返回的是true,所以TaskService会被加入到actualDependentBeans当中,最终抛出BeanCurrentlyInCreationException异常
    • 简单来说,spring认为如果一个bean在initializeBean前后不一致,并且一个已经完全初始化的beanA注入了这个未完全初始化的beanB,在spring的流程中beanA就再也没有机会改变注入的依赖了,所以会抛异常。
    • 而如果先实例化TaskService再实例化MessageService,就不会有这个问题(不信可以将TaskService改成ATaskService试试),因为如果在实例化TaskService的时候没有发现提前暴露出来的MessageService,就会专注于创建MessageService的过程,实例化并初始化完成后才会回到TaskService并将MessageService注入

为什么@Lazy可以解决这个问题

@Lazy 被大多数人理解为:当使用到的时候才会加载这个类。

这个也算是spring希望我们看到的,但是这个描述实际上不完全准确。举个例子:


@Service
public class TaskService {@Resource    @Lazyprivate MessageService messageService;  public void shit(){System.out.println();}
}
    • 这里在messageService属性上面加了@Lazy。在实例化TaskService,并populateBean的时候,在 CommonAnnotationBeanPostProcessor 的 getResourceToInject方法中, spring发现messageService被@Lazy注解修饰,便会将其包装成一个代理对象:即创建一个TargetSource,重写getTarget方法,返回的是 CommonAnnotationBeanPostProcessor 里的 getResource(beanName)方法(方法体中的逻辑,可以理解为从工厂的三层缓存中获取对象)。也就是说,注入给TaskService的是一个MessageService的代理对象(这是本文出现的第三种代理场景)。
    • 而spring在实例化MessageService的时候,不会管他是否是由@Lazy 修饰的,只会将其当做一个普通的bean去创建,成功后就会放入一级缓存(所以严格来讲,不能说是“使用到了再去加载”)。
    • 容器启动完成后,TaskService在需要使用messageService的方法时,会执行代理对象的逻辑,获取到TargetSource,调用getResource从三层缓存中获取messageService的真实对象,由于messageService此时已经被spring完整地创建好了,处于一级缓存singletonObjects当中,因此拿到之后可以放心使用。

致谢

感谢@挡不住的牛味浓 的分享。原文链接

http://www.yidumall.com/news/63751.html

相关文章:

  • 做企业云网站的企业邮箱成都私人网站建设
  • wordpress css保存百中搜优化
  • uugaicomlogo免费设计网站南宁百度关键词排名公司
  • 专业苏州网站建设北京网站建设公司案例
  • 学院网站建设报价企业培训考试系统app
  • 监狱门户网站的建设旅游景区网络营销案例
  • 怎样创办一个网站seo北京网站推广
  • 配置wordpress淮北seo
  • 政府蒙古文网站建设汇报材料苏州seo关键词优化排名
  • 龙港做网站网推项目接单平台
  • 龙采做网站要多少钱如何自己搭建网站
  • 西安网站运营什么是营销模式
  • 赣州人才网最新招聘信息网网络优化工程师前景如何
  • 长沙网站建设服务商百度移动seo首选帝搜软件
  • 杨凌网站建设推广百度代理查询系统
  • wordpress 压缩gif插件googleseo优化
  • 有域名怎么建网站百度竞价推广怎么收费
  • 网站页面优化seo搜索引擎优化书籍
  • 高端网站建设公司兴田德润在那里商品营销推广的方法有哪些
  • 网站建设哪家公司比较好搜狗网站seo
  • 网站建设自学多长时间百度网站官网入口网址
  • 做网站需要买什么百度收录申请
  • 做worksheet的网站怎么学做电商然后自己创业
  • 自己怎么做网上注册免费的网站百度竞价推广登录
  • 网站建设与单位干部作风的关系广告推广方案怎么写
  • 网站建设模板项目营销推广方案
  • 无锡高端网站建设开发软件开发培训班
  • 用php做一网站有哪些广告策划方案范文
  • 怎么做网站公众号seo有什么作用
  • 如何打开网站网页赣州seo