Java 如何解决Spring上下文的循环依赖关系?

Java 如何解决Spring上下文的循环依赖关系?,java,spring,kotlin,Java,Spring,Kotlin,我有三门课: open class RedirectProcessor( private val adProcessor: AdProcessor ) { fun run(depth: Int): String = if (depth < 3) adProcessor.run(depth + 1) else "redirect" } 因此,它们相互依赖。我尝试如下配置spring上下文: @Configuration class Config {

我有三门课:

open class RedirectProcessor(
    private val adProcessor: AdProcessor
) {

    fun run(depth: Int): String = 
        if (depth < 3) adProcessor.run(depth + 1) else "redirect"
}
因此,它们相互依赖。我尝试如下配置spring上下文:

@Configuration
class Config {

    @Bean
    @Lazy
    fun redirectProcessor(): RedirectProcessor = RedirectProcessor(adProcessor())

    @Bean
    @Lazy
    fun fallbackProcessor(): FallbackProcessor = FallbackProcessor(adProcessor())

    @Bean
    fun adProcessor() = AdProcessor(
        redirectProcessor = redirectProcessor(),
        fallbackProcessor = fallbackProcessor()
    )
}
我知道我必须使用@Lazy注释。如果我用@Component annotation标记我的服务,并在构造函数中使用@Lazy,它就可以正常工作。但是我需要使用@Bean注释定义Bean,这会导致问题。有什么办法可以解决这个问题吗?

我不能说是Kotlin(目前我对Kotlin的了解非常有限),而是使用Java和最新的spring版本(5.2.6.RELEASE)

我已经将它与您的示例的以下“kotlin to java”翻译一起使用:

public class RedirectProcessor {
    private final AdProcessor adProcessor;

    public RedirectProcessor(AdProcessor adProcessor) {
        this.adProcessor = adProcessor;
    }

    public String run(int depth) {
        if(depth < 3) {
            return adProcessor.run(depth + 1);
        }
        else {
            return "redirect";
        }
    }
}


public class FallbackProcessor {
    private final AdProcessor adProcessor;

    public FallbackProcessor(AdProcessor adProcessor) {
        this.adProcessor = adProcessor;
    }

    public String run(int depth) {
        if(depth < 3) {
            return adProcessor.run(depth + 1);
        }
        else {
            return "fallback";
        }
    }
}

public class AdProcessor {
    private RedirectProcessor redirectProcessor;
    private FallbackProcessor fallbackProcessor;

    public AdProcessor(RedirectProcessor redirectProcessor, FallbackProcessor fallbackProcessor) {
        this.redirectProcessor = redirectProcessor;
        this.fallbackProcessor = fallbackProcessor;
    }

    public String run (int depth) {
        return depth + redirectProcessor.run(depth) + fallbackProcessor.run(depth);
    }
}
注意
@Lazy
注释在参数上的用法,而不是在bean本身上

侦听器仅用于测试目的。运行应用程序将打印
23redirectfallback3redirectfallback

现在为什么它能工作

当spring看到这样一个
@Lazy
注释参数时,它会从参数类创建一个运行时生成的代理(使用CGLIB)

这个代理的作用方式是包装bean,并且只有在第一次“需要”时才会完全创建这个bean(请阅读,在本例中我们将调用这个bean的方法)

如果使用
@组件
,则与以下声明相同:

@Component
public class FallbackProcessor {
    private final AdProcessor adProcessor;

    public FallbackProcessor(@Lazy AdProcessor adProcessor) {
        this.adProcessor = adProcessor;
    }

    public String run(int depth) {
       ...
    }
}
另一方面,在上一个示例中,我没有将
@Autowired
放在
FallbackProcessor
类的构造函数上,只是因为如果只有一个构造函数,spring将“识别”并使用它注入所有依赖项


SO的和线程也可能是相关的(值得一读)。

我遇到了相同的问题,
@Autowire
注释由于某种原因无法工作,我不知道

因此,我使用了另一种解决方法:

  • 注入
    ApplicationContext
    ,而不是bean本身
  • ApplicationContext
代码如下:

class ServiceA(
    private val serviceB: ServiceB
) {
......
}


class ServiceB(
    private val applicationContext: ApplicationContext
) {
    private val serviceA: ServiceA by lazy {
        // we need this logic for only once
        // so "property delegated by lazy ..." is perfect for this purpose
        applicationContext.getBean(ServiceA::class.java)
    }
......
}


我试过这种方法。我得到了NPE,因为CGLIB代理在调用过程中没有被替换,所以它有没有可能在我的spring版本中修复了一个bug?也许你可以像我一样尝试spring的上一个版本?还是和科特林有关?(我已经测试了这个解决方案,它在我的机器上运行得很好)我尝试了不同的spring版本,结果是一样的。所以,我想这可能是由Kotlin引起的。所以,解决方案是将AdProcessor中的run方法标记为open
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }


    @Bean
    public RedirectProcessor redirectProcessor (@Lazy AdProcessor adProcessor) {
        return new RedirectProcessor(adProcessor);
    }

    @Bean
    public FallbackProcessor fallbackProcessor (@Lazy AdProcessor adProcessor) {
        return new FallbackProcessor(adProcessor);
    }

    @Bean
    public AdProcessor adProcessor (RedirectProcessor redirectProcessor, FallbackProcessor fallbackProcessor) {
        return new AdProcessor(redirectProcessor, fallbackProcessor);
    }

    @EventListener
    public void onApplicationStarted(ApplicationStartedEvent evt) {
        AdProcessor adProcessor = evt.getApplicationContext().getBean(AdProcessor.class);
        String result = adProcessor.run(2);
        System.out.println(result);
    }
}
@Component
public class FallbackProcessor {
    private final AdProcessor adProcessor;

    public FallbackProcessor(@Lazy AdProcessor adProcessor) {
        this.adProcessor = adProcessor;
    }

    public String run(int depth) {
       ...
    }
}
class ServiceA(
    private val serviceB: ServiceB
) {
......
}


class ServiceB(
    private val applicationContext: ApplicationContext
) {
    private val serviceA: ServiceA by lazy {
        // we need this logic for only once
        // so "property delegated by lazy ..." is perfect for this purpose
        applicationContext.getBean(ServiceA::class.java)
    }
......
}