Chilled_mimosa
Chilled_mimosa

Reputation: 79

SpringBoot application doesn't autowire field


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

Answers (6)

Norberto Ritzmann
Norberto Ritzmann

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

Matheus
Matheus

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

Dimitri Mestdagh
Dimitri Mestdagh

Reputation: 44685

You're using two bean declaration mechanisms:

  1. You're registering your bean using @Service
  2. You're registering a bean using @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

rascio
rascio

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

meistermeier
meistermeier

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

Tom Van Rossom
Tom Van Rossom

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

Related Questions