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

145
13 января 2019, 11:20

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

public class CCReport extends AbstractReport<CCReportRow> {
    public CCReport(List<CCReportRow> rows, LocalDate from, LocalDate to) {
        super(rows, from, to);
    }
    @Override
    public Class<CCReportRow> getRowClass() {
        return CCReportRow.class;
    }
}

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

public interface ReportService<T extends Report> {
    T createReport(LocalDate from, LocalDate to);
}

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

@Service
@RequiredArgsConstructor
public class CCReportService implements ReportService<CCReport> {
    private final CCReportsExportRepository repository;
    @Override
    public CCReport createReport(LocalDate from, LocalDate to) {
        return repository.generateReport(from, to);
    }
}

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

@Slf4j
@RequiredArgsConstructor
public abstract class AbstractReportTaskListener<T extends Report> implements ReportTaskListener<T> {
    protected final ReportService<T> reportService;
    //-------------------//    
    @Override
    public void processReportTask(SingleReportRequest<T> reportRequest) {
        Report report = reportService.createReport(reportRequest.getFrom(), reportRequest.getTo());
        //-------------------//
    }
}

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

@Component
public class RabbitCCReportTaskListener extends AbstractReportTaskListener<CCReport> {
    public RabbitCCReportTaskListener(
            ReportService<CCReport> 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"

Answer 1

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

В докер упакованы все jar-файлы проекта и все зависимости. Локально, вероятно, не все скомпилировано, попробуйте пересобрать проект начисто.

READ ALSO
Как вызвать метод java

Как вызвать метод java

Проблема в следующем, при выполнении кода метод (метод 1) вызывает другой метод (метод 2), но что-бы не увеличивать код нужно вызвать из "метода...

146
Java подключение библиотек

Java подключение библиотек

Немного не понимаю механизм подключения библиотеки, хочу подключить AGAVU, подскажите как ее добавить: достаточно в Maven добавить новую Dependency...

152
JSch addIdentity

JSch addIdentity

Скажите пожалуйста, как сгенерировать файл под jsch, я пробовал с помощью PuttyGen, но addIdentity вылетает с exception с сообщением, что файл-ключ "не правильный"?

137
Как работает такая проверка на четность/нечетность?

Как работает такая проверка на четность/нечетность?

Встретил пример, проверка числа i на нечетность и никак не могу понять как работает данная проверкаКонкретно не понятен блок (i&1)

170