Как разрешить круговую зависимость для контекста Spring? - PullRequest
2 голосов
/ 07 мая 2020

У меня есть три класса:

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 в конструкторе, он будет работать нормально. Но мне нужно определить beans с помощью аннотации @Bean, и это вызывает проблемы. Есть ли способ решить эту проблему?

1 Ответ

2 голосов
/ 10 мая 2020

Я не могу сказать о Kotlin (мои знания о kotlin довольно ограничены на данный момент), но о Java с последней доступной весенней версией (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

Теперь, почему это работает?

Когда Spring видит такой @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 в последнем примере, только потому, что, если есть единственный конструктор, spring "распознает это" и использует его для внедрения всех зависимостей.

в следующем руководстве и это несколько старое поток SO также может иметь значение (стоит прочитать).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...