Spring ApplicationContext.getBean возвращает неправильный класс

#java #spring

#java #spring

Вопрос:

Я ломал голову над этим, и я просто не могу понять, что не так.

У меня есть приложение Spring, которое использует ApplicationContext.getBean() для извлечения 2 похожих классов. Я получаю неправильный класс экземпляра из поиска компонента.

Вот класс ApplicationContext:

 public class DomainRegistryCab {
    private static ApplicationContext           applicationContext;
 
    private static ApplicationContext createApplicationContext() {
        return new AnnotationConfigApplicationContext( CaBridgeDomainServiceConfig.class );
    }   

    public static CertificateProductApplicationService certificateProductAppService() {
        var service = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
                applicationContext().getAutowireCapableBeanFactory(),
                CertificateProductApplicationService.class,
                CaBridgeDomainServiceConfig.CERTIFICATE_PRODUCT_APP_SERVICE);

//      var service = applicationContext().getBean(
//          CaBridgeDomainServiceConfig.CERTIFICATE_PRODUCT_APP_SERVICE,
//          CertificateProductApplicationService.class);
//      var service = applicationContext().getBean(CertificateProductApplicationService.class);
        validateDataSourceIs(DataSource.ProductDataStore, service.dataSource());
        return service;
    }

    public static CertificateProgramApplicationService certificateProgramAppService() {
        var service = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
                applicationContext().getAutowireCapableBeanFactory(),
                CertificateProgramApplicationService.class,
                CaBridgeDomainServiceConfig.CERTIFICATE_PROGRAM_APP_SERVICE);
//      var service = applicationContext().getBean(
//              CaBridgeDomainServiceConfig.CERTIFICATE_PROGRAM_APP_SERVICE,
//              CertificateProgramApplicationService.class);
//      service = applicationContext().getBean(CertificateProgramApplicationService.class);
        validateDataSourceIs(DataSource.ProgramDataStore, service.dataSource());
        return service;
    }
  

Вот CaBridgeDomainServiceConfig:

 @Configuration
@ComponentScan(basePackageClasses = { HibernateConfigurationMarker.class }) 
public class CaBridgeDomainServiceConfig {
    
    public static final String CERTIFICATE_PRODUCT_APP_SERVICE = "certificateProductAppService";
    public static final String CERTIFICATE_PROGRAM_APP_SERVICE = "certificateProgramAppService";
    
    @Bean(name= CERTIFICATE_PRODUCT_APP_SERVICE)
    public CertificateProductApplicationService certificateProductAppService() {
        return new CertificateProductApplicationServiceCabImpl();
    }

    @Bean(name= CERTIFICATE_PROGRAM_APP_SERVICE)
    public CertificateProgramApplicationService certificateProgramAppService() {
        return new CertificateProgramApplicationServiceCabImpl();
    }
}

public interface CertificateProductApplicationService extends CertificateApplicationService {
}
public interface CertificateProductApplicationService extends CertificateApplicationService {
}
public interface CertificateApplicationService {
}
  

Используя вышеуказанные классы, если я вызываю DomainRegistryCab.certificateProductAppService() Я получаю экземпляр CertificateProgramApplicationService, а не CertificateProductApplicationService .

Я получаю аналогичные результаты, если использую этот метод:

 public static CertificateProductApplicationService certificateProductAppService() {
    var service = applicationContext().getBean(
        CaBridgeDomainServiceConfig.CERTIFICATE_PRODUCT_APP_SERVICE,
        CertificateProductApplicationService.class);
    validateDataSourceIs(DataSource.ProductDataStore, service.dataSource());
    return service;
}
  

Я также пытался заставить методы @Bean возвращать классы реализации и ApplicationContext().getBean() запрашивать классы реализации вместо интерфейсов:

 public class DomainRegistryCab {
    private static ApplicationContext applicationContext;
     
    private static ApplicationContext createApplicationContext() {
    return new AnnotationConfigApplicationContext( CaBridgeDomainServiceConfig.class );
    }   
    
    public static CertificateProductApplicationService certificateProductAppService() {
    var service = applicationContext().getBean(CertificateProductApplicationServiceCabImpl.class);
        validateDataSourceIs(DataSource.ProductDataStore, service.dataSource());
        return service;
    }
    public static CertificateProgramApplicationService certificateProgramAppService() {
    var service = applicationContext().getBean(CertificateProgramApplicationServiceCabImpl.class);
        validateDataSourceIs(DataSource.ProgramDataStore, service.dataSource());
        return service;
    }
    public static ApplicationContext applicationContext() {
        if (applicationContext == null)
            applicationContext = createApplicationContext();
        return applicationContext;
    }
}

@ComponentScan(basePackageClasses = { HibernateConfigurationMarker.class }) 
public class CaBridgeDomainServiceConfig {
    @Bean(name= CERTIFICATE_PRODUCT_APP_SERVICE)
    public CertificateProductApplicationServiceCabImpl certificateProductAppService() {
    return new CertificateProductApplicationServiceCabImpl();
    }

    @Bean(name= CERTIFICATE_PROGRAM_APP_SERVICE)
    public CertificateProgramApplicationServiceCabImpl certificateProgramAppService() {
    return new CertificateProgramApplicationServiceCabImpl();
    }
}
  

Этот код приводит к тому, что spring вообще не находит классы реализации:

 org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cmb.cabridge.application.cert.CertificateProductApplicationServiceCabImpl' available
  

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

1. Я не вижу никаких проблем с вашим первым подходом. Можете ли вы попробовать отладку, распечатав все свои spring-beans после создания контекста приложения, чтобы проверить, какие компоненты присутствуют в контексте? См . baeldung.com/spring-show-all-beans

2. Да, я включил ведение журнала spring Framework для ОТЛАДКИ, и это показывает, что он находит оба компонента. Я также напечатал ApplicationContext.getbeandefinitionname(), и он также показывает оба компонента.

3. Можете ли вы воспроизвести его на github.com ? Какую версию spring вы используете?

4. К сожалению, мы не можем опубликовать его на github как собственное приложение. Использование springframework 5.16.RELEASE.

5. Только что обновлено до springframework 5.2.11.RELEASE и spring boot 2.3.6.RELEASE и все еще имеет ту же проблему.

Ответ №1:

В конце концов я смог заставить вещи работать, используя ApplicationContext().getBean(«beanName», CertificateProductApplicationService.class ). Проблема была глубже в моем коде в классе CertificateProductApplicationServiceCabImpl, который использовал и вернул неправильный источник данных.