Приложение SpringBoot не подключает поле autowire


У меня есть ServiceImpl, который выглядит так:

@Service
@RequiredArgsConstructor
public class ServiceAImpl implements ServiceA {
private final String fieldA;
@Override 
public boolean isFieldA(String text){
return fieldA.equals(text);
}

И я хотел бы ввести значение поля в fieldA в Application.java из application.yml следующим образом:

@EnableSwagger2
@SpringBootApplication
public class Application {
@Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
}
@Bean
public ServiceA serviceA() {
    return new ServiceAImpl(fieldA);
}

Но при запуске приложения SpringBoot появляется следующая ошибка:

Error creating bean with name 'serviceAImpl' defined in URLNo qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

У вас есть какое-нибудь решение?

попробуйте использовать квалификатор

Sagar Kharab 04.06.2018 16:04
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
1
1 075
6

Ответы 6

Вы используете два механизма объявления bean-компонентов:

  1. Вы регистрируете свой bean-компонент с помощью @Service
  2. Вы регистрируете bean-компонент с помощью @Bean

Это означает, что ваш сервис будет создан дважды. Тот, который определен с помощью @Bean, работает правильно, поскольку он использует аннотацию @Value для вставки правильного значения в вашу службу.

Однако служба, созданная из-за @Service, не знает об аннотации @Value и попытается найти любой bean-компонент типа String, который не может найти, и, таким образом, выдаст исключение, которое вы видите.

Теперь решение состоит в том, чтобы выбрать любой из них. Если вы хотите сохранить конфигурацию @Bean, вам следует удалить аннотацию @Service из ServiceAImpl, и это поможет.


В качестве альтернативы, если вы хотите сохранить аннотацию @Service, вам следует удалить объявление @Bean, и вы должны написать свой собственный конструктор, а не полагаться на Lombok, потому что это позволяет вам использовать аннотацию @Value в конструкторе:

@Service
public class ServiceAImpl implements ServiceA {
    private final String fieldA;

    /**
     * This constructor works as well
     */
    public ServiceAImpl(@Value("${fieldA}") String fieldA) {
        this.fieldA = fieldA;
    }

    @Override 
    public boolean isFieldA(String text){
        return fieldA.equals(text);
    }
}

@ MaciejPapurzyński, какое решение вы выбрали? Вы удалили аннотацию @Service или вы удалили @Bean?

g00glen00b 04.06.2018 16:26

Попробуй это

@Service
public class ServiceAImpl implements ServiceA { 
    private final String fieldA;

    @Autowire
    public ServiceAImpl(@Value("${fieldA}") String fieldA){
        this.fieldA = fieldA;
    }

    @Override 
    public boolean isFieldA(String text){
        return fieldA.equals(text);
    }
}

и это

@EnableSwagger2
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Вы не должны использовать @Service и @Bean для одного и того же класса!

Вы аннотировали свой класс с помощью @Service и определили его вручную как компонент с аннотацией @Bean. Я думаю, что второе - это то, как вы планировали его использовать. Аннотации @Service заставят этот класс подхватить сканирование компонентов Spring и дополнительно создать его экземпляр. Конечно, он пытается разрешить параметры и терпит неудачу, когда пытается найти соответствующий «bean-компонент» для поля String, потому что простого String bean-компонента нет (и не должно :)).

Удалите аннотацию @Service, и все должно работать как положено.

Spring не такая уж и умная :)

Вы должны аннотировать свой bean-компонент следующим образом:

@RequiredArgsConstructor
public class ServiceAImpl {
     @Value("${fieldA}")
     private final String something;
...

Но я не уверен, что это будет работать с @RequiredFieldsConstructor, было бы проще записать конструктор с аннотацией @Autowired и использовать аннотацию @Value для параметра String:

@Autowired
public ServiceAImpl(@Value("${aProp}") String string) {

Небольшое примечание: аннотация @Autowired больше не нужна для конструкторов, начиная с Spring 4.3 / Spring boot 1.3 (по крайней мере, если у вас есть только один конструктор).

g00glen00b 04.06.2018 16:19

Это сообщение об ошибке дается из-за того, что он пытается автоматически связать параметр конструктора с Spring beans, но если вам нужно передать эту строку как @Value, вам нужно поместить аннотацию в аргумент конструктора, но если она сгенерирована lombok он не может поместить эту аннотацию

rascio 06.06.2018 17:22

Если вы хотите объявить ServiceAImpl как bean-компонент Spring в файле конфигурации Java, вам следует удалить аннотацию @Service из объявления класса. Эти аннотации не работают вместе.

ServiceAImpl.java

import org.springframework.beans.factory.annotation.Autowired;

public class ServiceAImpl implements ServiceA {

    private final String fieldA;

    @Autowired
    public ServiceAImpl(String fieldA) {
        this.fieldA = fieldA;
    }

    @Override
    public boolean isFieldA(String text) {
        return fieldA.equals(text);
    }
}

Application.java

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Application {
    @Value("${fieldA}")
    private String fieldA;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public ServiceA serviceA() {
        return new ServiceAImpl(fieldA);
    }
}

Ваш application.properties

fieldA=value

Приведенная ниже реализация мне подходит. У вас есть две проблемы: сначала вам нужно выбрать между @Service и @Bean, а другой проблемой, которую я видел в вашем коде, была аннотация @Value, которую вы должны использовать только для ввода значения из свойств.

@SpringBootApplication
public class TestedValueApplication {

    @Autowired
    void printServiceInstance(ServiceA service) {

        System.out.println("Service instance: " + service);
        System.out.println("value==value? " + service.isFieldA("value"));
    }

    public static void main(String[] args) {
        SpringApplication.run(TestedValueApplication.class, args);

    }

    @Bean
    public ServiceA serviceA(@Value("${fieldA}") String fieldA) {
        return new ServiceAImpl(fieldA);
    }
}

Услуга:

public class ServiceAImpl implements ServiceA {

    private String fieldA;

    ServiceAImpl(String fieldA) {
        this.fieldA = fieldA;
    }

    public boolean isFieldA(String text) {
        return fieldA.equals(text);
    }
}

application.properties:

fieldA=value

Другие вопросы по теме