Страницы

Поиск по вопросам

понедельник, 30 марта 2020 г.

Неявное поведение spring при работе с дженериками

#java #spring #docker #spring_boot


Есть некая сущность Report и одна из ее реализаций:

public class CCReport extends AbstractReport {

    public CCReport(List rows, LocalDate from, LocalDate to) {
        super(rows, from, to);
    }

    @Override
    public Class getRowClass() {
        return CCReportRow.class;
    }
}


Есть некий интерфейс сервиса: 

public interface ReportService {
    T createReport(LocalDate from, LocalDate to);
}


У него есть несколько реализаций в зависимости от типа Report:

@Service
@RequiredArgsConstructor
public class CCReportService implements ReportService {

    private final CCReportsExportRepository repository;

    @Override
    public CCReport createReport(LocalDate from, LocalDate to) {
        return repository.generateReport(from, to);
    }
}


Далее есть абстрактная реализация слушателя RabbitMq:

@Slf4j
@RequiredArgsConstructor
public abstract class AbstractReportTaskListener implements ReportTaskListener {

    protected final ReportService reportService;
    //-------------------//    

    @Override
    public void processReportTask(SingleReportRequest reportRequest) {
        Report report = reportService.createReport(reportRequest.getFrom(), reportRequest.getTo());
        //-------------------//
    }
}


И непосредственно сама реализация этого слушателя:

@Component
public class RabbitCCReportTaskListener extends AbstractReportTaskListener {

    public RabbitCCReportTaskListener(
            ReportService reportService,
            ReportMailService mailService,
            ReportToExcelService excelService,
            ReportsNameGenerator nameGenerator,
            WebDavStorageService webDavStorageService) {
        super(reportService, mailService, excelService, nameGenerator, webDavStorageService);
    }

    @RabbitListener(queues = RabbitConfig.CC_QUEUE, concurrency = "2-2", autoStartup
= "true")
    @Override
    public void processReportTask(SingleReportRequest reportRequest) {
        super.processReportTask(reportRequest);
    }
}


При запуске всего этого добра через gradle bootRun все запускается без проблем, однако,
если создаю docker-образ вылетает ошибка:


  *********************** APPLICATION FAILED TO START
  
  
  
  Description:
  
  Parameter 0 of constructor in
  com.reports.service.RabbitCCReportTaskListener required a single bean,
  but 2 were found:
   - CCReportService: defined in URL [jar:file:/app/reports.jar!/BOOT-INF/classes!/reports/service/CCReportService.class]
   - SRReportService: defined in URL [jar:file:/app/reports.jar!/BOOT-INF/classes!/reports/service/SRReportService.class]
  
  Action:
  
  Consider marking one of the beans as @Primary, updating the consumer
  to accept multiple beans, or using @Qualifier to identify the bean
  that should be consumed


В конце концов приходится явно описывать бины через @Qualifier и тогда начинает работать.
Подскажите, с чем может быть связано такое поведение?

UPD:
Вот ссылка на SO, где уже описывали подобный вопрос. Плюс есть несколько статей по
запросу "spring generic bean autowire"
    


Ответы

Ответ 1



Это случается из-за Type Erasure: при компиляции ReportService вырождается в ReportService, и здесь возникает неоднозначность в рантайме, т.к. таких бинов уже 2. Чтобы избежать ситуации используйте либо конкретный наследник ReportService либо @Qualifier в конструкторе конкретного наследника AbstractReportTaskListener. В докер упакованы все jar-файлы проекта и все зависимости. Локально, вероятно, не все скомпилировано, попробуйте пересобрать проект начисто.

Комментариев нет:

Отправить комментарий