#java #spring #spring-boot
#java #весна #весенняя загрузка #spring
Вопрос:
У меня странная ситуация. Я пытаюсь написать класс автоматической настройки, в котором я создаю компонент, если другой компонент существует или нет, поэтому у меня есть условие для компонента и условие для отсутствующего компонента для того же типа..
Вот мой код:
@Bean
@ConditionalOnMissingBean(
type = {"com.example.Api"}
)
public ApiManager apiManager() {
return new ApiManager() {
public Result getValue(Request request) {
throw new Exception("Api not provided.");
}
public Result setValue(Request request) {
throw new Exception("Api not provided.");
}
};
}
@Bean
@ConditionalOnBean(
type = {"com.example.Api"}
)
public ApiManager apiManager(Api api) {
return new ApiManagerImpl(api);
}
Проблема в том, что он не проверяет, @ConditionalOnBean
если он уже проверил, @ConditionalOnMissingBean
что компонент типа com.example.Api
не отсутствует, а затем компонент не создается.
И я получаю ошибку типа:
Parameter 2 of constructor in com.example.ServiceImpl required a bean of type 'com.example.ApiManager' that could not be found.
- Bean method 'apiManager' in 'ApiAutoConfiguration' not loaded because @ConditionalOnMissingBean (types: com.example.Api; SearchStrategy: all) found bean 'api'
- Bean method 'apiManager' in 'ApiAutoConfiguration' not loaded because @ConditionalOnMissingBean (types: com.example.Api; SearchStrategy: all) found bean 'api'
Ответ №1:
То, что вы получаете, вполне логично. Условия оцениваются по порядку, поэтому автоконфигурации обрабатываются после настройки пользователем.
Если бы контекст выполнял то, что вы ожидаете, нам пришлось бы оценить все условия всех определений компонентов, прежде чем что-либо делать. И это сведет на нет цель автоматической настройки, обрабатывающей тот факт, что компонент определенного типа был предоставлен пользователем (user config) или автоматической конфигурацией.
Вам нужно переписать эту автоматическую конфигурацию, чтобы импортировать вещи по порядку. Один из способов сделать это — переместить каждый метод в класс частной конфигурации пакета, а затем выполнить следующее:
@Configuration
@Import({ApiPresentConfiguration.class, ApiNotPresentConfiguration.class})
Лучший способ справиться с этим на самом деле — вообще не делать этого и просто использовать ObjectProvider
:
@Bean
public ApiManager apiManager(ObjectProvider<Api> api) {
// Check if API is present and then return the appropriate implementation
}
Комментарии:
1. Я ожидал, что он будет работать с
ObjectProvider
, но все еще не работает с использованием условных обозначений… При использовании с@Import
должен ли я использовать внутренниеApiPresentConfiguration
условные обозначения?2. Импорт — это просто упорядочивание того, как вы хотите, чтобы условия оценивались.
3.Используя импорт, он вводится,
ApiNotPresentConfiguration
ноApi
компонент создается другим,AutoConfiguration
поэтому порядок неправильный, да?Api
AutoConfiguration
Должно быть выполнено передApiMenagerAutoConfiguration
да?
Ответ №2:
попробуйте использовать @Primary
.
Я не уверен, как это будет соответствовать вашему варианту использования, но рассмотрите следующее:
@Bean
public ApiManager apiManager() {
return new ApiManager() {
public Result getValue(Request request) {
throw new Exception("Api not provided.");
}
public Result setValue(Request request) {
throw new Exception("Api not provided.");
}
};
}
@Bean
@ConditionalOnBean(
type = {"com.example.Api"}
)
@Primary
public ApiManager apiManager(Api api) {
return new ApiManagerImpl(api);
}
Случай 1: Api
не существует
- Первый компонент действует как компонент по умолчанию и используется.
- Второй компонент вообще не создается.
Случай 2: Api
существует
- Создается первый компонент (поскольку он используется по умолчанию), но не используется.
- Создается второй компонент, и в итоге вы получаете 2 компонента типа
ApiManager
; но поскольку у него есть@Primary
; он будет использоваться.