bugfix> java > 投稿

私には3つのクラスがあります:

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を定義する必要があり、問題が発生します。それを解決する方法はありますか?

回答 2 件
  • Kotlinについては言えませんが(kotlinに関する私の知識は現時点ではかなり限られています)、Javaでは最後に利用可能なSpringバージョン(5.2.6.RELEASE)があります。

    私はあなたの例の次の「kotlinから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

    なぜそれが機能するのですか?

    春がそのようなものを見たとき @Lazy 注釈付きパラメーター-パラメータークラスからランタイム生成プロキシ(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 最後の例のクラスは、コンストラクターが1つしかない場合にのみ、Springが「それを認識」し、それを使用してすべての依存関係を挿入するためです。

    次のチュートリアルとSOのこのやや古いスレッドも関連している可能性があります(読む価値があります)。

  • 私は同じ問題に遭遇しました、そして @Autowire なんらかの理由で注釈が機能しません。

    そこで、別の回避策を使用しました。

    注入する ApplicationContext Bean自体の代わりに

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

あなたの答え