簡體   English   中英

如何解決 Spring 上下文的循環依賴?

[英]How to resolve circular dependency for Spring context?

我有三個班級:

open class RedirectProcessor(
    private val adProcessor: AdProcessor
) {

    fun run(depth: Int): String = 
        if (depth < 3) adProcessor.run(depth + 1) else "redirect"
}
open class FallbackProcessor(
    private val adProcessor: AdProcessor
) {

    fun run(depth: Int): String = 
        if (depth < 3) adProcessor.run(depth + 1) else "fallback"
}
open class AdProcessor(
    private val redirectProcessor: RedirectProcessor,
    private val fallbackProcessor: FallbackProcessor
) {

    fun run(depth: Int): String = 
        depth.toString() + 
        redirectProcessor.run(depth) +
        fallbackProcessor.run(depth)
}

因此,它們相互依賴。 我嘗試配置 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 注釋標記我的服務並在構造函數中使用 @Lazy 它工作正常。 但是我需要使用 @Bean 注釋來定義 bean,這會導致問題。 有什么辦法可以解決嗎?

I can't say for Kotlin (my knowledge of kotlin is rather limited at this point), but in Java with the last available spring version (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);
    }
}

然后訣竅是以不同的方式使用配置(但從 Java 配置規則的角度來看,這是完全“合法”的方式):

@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);
    }
}

注意@Lazy注釋在參數上的使用,而不是在 bean 本身上。

偵聽器僅用於測試目的。 運行應用程序打印23redirectfallback3redirectfallback

現在為什么它起作用了?

當 spring 看到這樣的@Lazy注釋參數時,它會根據參數 class 創建運行時生成的代理(使用 CGLIB)。

此代理以包裝 bean 的方式運行,並且僅當它第一次“需要”時才會完全創建此 bean(請閱讀,在這種情況下我們將調用此 bean 的方法)。

如果您使用@Component它與以下聲明相同:

@Component
public class FallbackProcessor {
    private final AdProcessor adProcessor;

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

    public String run(int depth) {
       ...
    }
}

附帶說明一下,我沒有在最后一個示例中將@Autowired放在FallbackProcessor class 的構造函數上,只是因為如果有一個構造函數 spring 將“識別”並使用它來注入所有依賴項。

下面的教程這個有點老的 SO 線程也可能是相關的(值得一讀)。

我遇到了同樣的問題, @Autowire注釋由於某種我不知道的原因不起作用。

所以我使用了另一種解決方法:

  • 注入ApplicationContext而不是 bean 本身
  • ApplicationContext檢索 bean 實例

代碼如:

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)
    }
......
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM