У меня есть 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: {}
У вас есть какое-нибудь решение?




Вы используете два механизма объявления bean-компонентов:
@Service@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?
Попробуй это
@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 (по крайней мере, если у вас есть только один конструктор).
Это сообщение об ошибке дается из-за того, что он пытается автоматически связать параметр конструктора с Spring beans, но если вам нужно передать эту строку как @Value, вам нужно поместить аннотацию в аргумент конструктора, но если она сгенерирована lombok он не может поместить эту аннотацию
Если вы хотите объявить 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
попробуйте использовать квалификатор