重要提示:当开启@EnableAsync,并使用@Async异步线程后,即便通过使用set方式注入,也会存在循环依赖导致无法启动项目,报错: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 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.

 

意味着所有循环依赖被注入的地方都必须使用 @Lazy延迟加载,通常改动的地方非常多,建议不使用@EnableAsync,@Async异步线程,而使用其他工具类代替,比如CompletableFuture。

 

 

 

(推荐)方法0:直接注入(springboot 2.6及以上需要设置允许循环依赖,以下则默认允许)

 

#设置允许循环依赖
spring: main: allow-circular-references: true


//代码

 

private TestService testService;

@Autowired
public void setTestService(TestService testService) {
this.testService = testService;
}

 

 

(不推荐)方法1:每次调用的时候使用AopContext.currentProxy()

 

 

基于 proxy 的 spring aop 带来的内部调用问题可以使用 AopContext.currentProxy() 强转为当前的再调用就可以解决了

例如:


错误用法:
public Account getAccountByName2(String userName) {
  return this.getAccountByName(userName);
}


修改为:

public Account getAccountByName2(String userName) {
  return ((AccountService)AopContext.currentProxy()).getAccountByName(userName);
}

 

另外注意:要设置aop实体暴露出来。在springboot的application.java里面加上@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)

 

 

(推荐)方法2:利用初始化方法在目标对象中注入代理对象

 

在目标对象类中注入spring上下文,通过context获取代理对象,并调用代理对象的方法。

注意:该方案对于scope为prototype的bean无法适用,因为每次获取bean时都返回一个新的对象。

  方法2.1:延迟加载方式

 
private TestService testService;

@Autowired
@Lazy
public void setTestService(TestService testService) {
this.testService = testService;
}


   方法2.2:初始化时,通过ApplicationContext获取对象

 

import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import com.blog.common.aop.service.TestService;

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private ApplicationContext context;

    private TestService testService;

    @PostConstruct
    // 初始化方法,在IOC注入完成后会执行该方法
    private void init() {
        // 从spring上下文获取代理对象(直接通过testService=this是不对的,this是目标对象)
        // 此种方法不适合于prototype Bean,因为每次getBean返回一个新的Bean
        testService = context.getBean(TestService.class);
    }

    public void methodA() throws Exception {
        System.out.println("method A run");
        System.out.println("method A 中调用method B,通过注入的代理对象,调用代理对象的方法,解决内部调用实现的问题。");
        testService.methodB(); //调用代理对象的方法,解决内部调用失效的问题
    }

    public void methodB() {
        System.out.println("method B run");
    }


}

 

注意: 循环依赖也可以通过@Lazy解决,务必在互相引入的双方注入都要加上@Lazy。spring-boot 2.6及以上后默认禁用循环依赖,可以通过以下配置使支持循环依赖

spring:
  main:
    allow-circular-references: true

 

posted on 2018-08-16 12:12  花开浪漫拾  阅读(3594)  评论(0编辑  收藏  举报