question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Injecting beans from auto-configuration by their actual type fails

See original GitHub issue

Lets assume following auto-configuration class:

@AutoConfiguration
public class PersonAutoConfiguration {

    @Bean
    PersonService personService() {
        return new PersonServiceImpl();
    }
}

Injecting personService bean by PersonServiceImpl type:

@Component
public class MyComponent {
    private final PersonServiceImpl personService;

    MyComponent(PersonServiceImpl personService) {
        this.personService = personService;
    }
}

fails with:

No qualifying bean of type '...PersonServiceImpl' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

Injecting through an interface type works without problems. Also, injecting by actual type (PersonServiceImpl) works for test classes and applicationContext.getBean(PersonServiceImpl.class) returns an actual bean.

As far as I can see only beans created through auto-configurations are affected. Beans created through regular configurations are injectable through their interfaces and actual classes.

This behaviour has not been observed in Spring Boot 2.7.4 but is reproducible from Spring Boot 3.0.0-M3 (I am not able to run build with older milestones due to missing dependencies).

~Sample project that reproduces this issue coming soon.~

Sample project that reproduces this issue: https://github.com/maciej-scratches/spring-boot-gh-32763 (./mvnw verify to see the failing test).

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:2
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

3reactions
wilkinsonacommented, Oct 18, 2022

As far as I know, it has always been possible for this to happen.

Your @Bean method is hiding the type of the personService bean from the bean factory. Up until the point that the bean is created, the bean factory will only know that it’s a PersonService and injection of a PersonServiceImpl will fail. Once the personService bean has been created, the bean factory learns that it is, in fact, a PersonServiceImpl and injection of a PersonServiceImpl will then succeed. In other words if the personService bean is created before MyComponent, it will work. If MyComponent is created first it will fail.

It’s possible that a change somewhere (I suspect Spring Framework to be the most likely place) has made it more likely that MyComponent is created first and for the failure to occur. However, you shouldn’t rely on a particular ordering and should instead make sure that the signature of your @Bean method uses the most-specific type possible for its return type. There is a tip about this in Boot’s reference documentation:

When declaring a @Bean method, provide as much type information as possible in the method’s return type. For example, if your bean’s concrete class implements an interface the bean method’s return type should be the concrete class and not the interface. Providing as much type information as possible in @Bean methods is particularly important when using bean conditions as their evaluation can only rely upon to type information that is available in the method signature.

2reactions
wilkinsonacommented, Oct 18, 2022

It’s particularly important for auto-configuration and bean conditions as the conditions are evaluated before beans are created. That means that the only type information that’s available is from the signature of the @Bean method. This is really a separate problem to the dependency injection failure that you had.

In terms of dependency injection, the problem can occur in any Spring app. It depends entirely on the order in which the beans are created. Without explicit or implicit dependencies between beans, the ordering isn’t really guaranteed and the problem can occur if the beans are created in the “wrong” order.

Thanks for spotting the problem with KafkaAutoConfiguration. I’ve opened https://github.com/spring-projects/spring-boot/issues/32766.

Because code samples where the interface is a return type are even in Spring Framework docs

Here’s an example from the Framework docs:

@Configuration
public class AppConfig {

   @Bean
   public MyService myService() {
       return new MyServiceImpl();
   }
}

This isn’t necessarily wrong. It depends on whether or not you want to be able to inject MyServiceImpl or only ever inject MyService. That said, using MyServiceImpl as the return type won’t do any harm and, generally speaking, it’s what I would recommend. There are a few rare edge cases, such as if MyServiceImpl is private and you’re using AOT/Native.

From the Framework docs again:

The preceding AppConfig class is equivalent to the following Spring <beans/> XML:

<beans>
   <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

Arguably, this is wrong. In the XML case, the bean factory will know that the bean’s type is com.acme.services.MyServiceImpl from the outset. I’ve opened https://github.com/spring-projects/spring-framework/issues/29338.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Error creating bean in dependency injection with spring-boot
I have UserRepository class in info.iyngaran.core.auth.repository package and it is annotated with @Repository . When I try to inject it in ...
Read more >
Fix No Qualifying Spring Bean Error For Spring Boot Tests
Reasons and solutions for no qualifying Spring Bean errors when writing tests for Spring Boot applications that include a Spring Test ...
Read more >
When two beans collide - Tratif
The only rule is this: bean with the same name as another one, which is processed later, overrides the older one. Sanity goes...
Read more >
Management & Monitoring - Micronaut Documentation
You can now inject LocalizedMessageSource , a @RequestScope bean, in your controllers to resolve localized messages for the current HTTP Request.
Read more >
Consider defining a bean of type 'com.xxx ... - GitHub
The injection point has the following annotations: - @org.springframework.beans.factory.annotation.Autowired(required=true) Action: Consider ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found