Reputation: 79
I do have ServiceImpl which looks like this:
@Service
@RequiredArgsConstructor
public class ServiceAImpl implements ServiceA {
private final String fieldA;
@Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
And I would like to inject a field value to fieldA in an Application.java from application.yml like this:
@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);
}
But I receive the following error when running SpringBoot app:
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: {}
Do you have any solution for that?
Upvotes: 2
Views: 1366
Reputation: 419
The below implementation works well for me. You have two issues, first you have to choose between @Service and @Bean and the other issue I've seen in your code was the @Value annotation, you have to use only to inject a value from the properties.
@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);
}
}
Service:
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
Upvotes: 0
Reputation: 3370
If you want to declare ServiceAImpl
as a Spring bean in your Java Configuration file, you should remove the @Service
annotation from the class declaration. These annotations doesn't work well together.
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);
}
}
Your application.properties
fieldA=value
Upvotes: 0
Reputation: 44685
You're using two bean declaration mechanisms:
@Service
@Bean
This means that your service will be created twice. The one defined using @Bean
works properly, since it uses the @Value
annotation to inject the proper value in your service.
However, the service created due to @Service
doesn't know about the @Value
annotation and will try to find any bean of type String
, which it can't find, and thus it will throw the exception you're seeing.
Now, the solution is to pick either one of these. If you want to keep the @Bean
configuration, you should remove the @Service
annotation from ServiceAImpl
and that will do the trick.
Alternatively, if you want to keep the @Service
annotation, you should remove the @Bean
declaration, and you should write your own constructor rather than relying on Lombok because this allows you to use the @Value
annotation within the constructor:
@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);
}
}
Upvotes: 1
Reputation: 9279
Spring is not so smart :)
You should annotate your bean like:
@RequiredArgsConstructor
public class ServiceAImpl {
@Value("${fieldA}")
private final String something;
...
But I'm not sure it will work with the @RequiredFieldsConstructor
, it would be simpler for you write down the constructor annotated with @Autowired
and using the @Value
annotation for the String
parameter:
@Autowired
public ServiceAImpl(@Value("${aProp}") String string) {
Upvotes: 1
Reputation: 8262
You annotated your class with @Service
and defined it manually as a bean with the @Bean
annotation. I do think the second is the way you planned to use it.
The @Service
annotation will make this class get picked up by Spring's component scan and additionally create an instance of it.
Of course it tries to resolve the parameters and fails when it tries to find a matching "bean" for the String field because there is no simple String bean (and should not :) ).
Remove the @Service
annotation and everything should work as expected.
Upvotes: 2
Reputation: 1470
Try this
@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);
}
}
and this
@EnableSwagger2
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You should not use @Service
and @Bean
for the same class!
Upvotes: 1