springboot-how to solve NoSuchBeanDefinitionException: No qualifying bean of type available exception when using spring framework with autowire

Problem

When we run a spring application with autowire injection, sometimes, this error would occur:

16:17:44: Executing task 'bootRun'...

> Task :app9:compileJava UP-TO-DATE
> Task :app9:processResources UP-TO-DATE
> Task :app9:classes UP-TO-DATE
> Task :app9:bootRunMainClassName UP-TO-DATE

> Task :app9:bootRun
#####   ####  #    # ###### #    #      ####   ####  #    #
#    # #      #    # #      ##   #     #    # #    # ##  ##
#####   ####  #    # #####  # #  #     #      #    # # ## #
#    #      # # ## # #      #  # # ### #      #    # #    #
#    # #    # ##  ## #      #   ## ### #    # #    # #    #
#####   ####  #    # ###### #    # ###  ####   ####  #    #
2021-02-22 16:17:46.658  INFO 15763 --- [  restartedMain] com.bswen.app9.Main                      : Starting Main using Java 1.8.0_121 on bswen.local with PID 15763 (/Users/bswen/bswen-github/bswen-springboot23/app9/build/classes/java/main started by bswen in /Users/bswen/private/bw/bswen-github/bswen-springboot23/app9)
2021-02-22 16:17:46.664  INFO 15763 --- [  restartedMain] com.bswen.app9.Main                      : No active profile set, falling back to default profiles: default
2021-02-22 16:17:46.707  INFO 15763 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2021-02-22 16:17:46.707  INFO 15763 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2021-02-22 16:17:47.609  INFO 15763 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-02-22 16:17:47.621  INFO 15763 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-02-22 16:17:47.621  INFO 15763 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-02-22 16:17:47.679  INFO 15763 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-02-22 16:17:47.679  INFO 15763 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 971 ms
2021-02-22 16:17:47.771  WARN 15763 --- [  restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webSecurityConfig': Unsatisfied dependency expressed through field 'customAuthentionProvider'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.bswen.app9.customsecurity.CustomAuthentionProvider' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
2021-02-22 16:17:47.773  INFO 15763 --- [  restartedMain] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2021-02-22 16:17:47.783  INFO 15763 --- [  restartedMain] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-02-22 16:17:47.798 ERROR 15763 --- [  restartedMain] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field customAuthentionProvider in com.bswen.app9.config.WebSecurityConfig required a bean of type 'com.bswen.app9.customsecurity.CustomAuthentionProvider' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.bswen.app9.customsecurity.CustomAuthentionProvider' in your configuration.


BUILD SUCCESSFUL in 2s
4 actionable tasks: 1 executed, 3 up-to-date
16:17:48: Task execution finished 'bootRun'.


The core exception is:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.bswen.app9.customsecurity.CustomAuthentionProvider' available: expected at least 1 bean which qualifies as autowire candidate.

Spring framework is complaining that there should be a bean named CustomAuthentionProvider , it can not find it.

But I am sure everything is correctly configured, and the codes should be ok.

Environment

  • SpringBoot 1.x and 2.x

The codes

Let’s check the souce code of CustomAuthentionProvider:


public class CustomAuthentionProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = String.valueOf(authentication.getCredentials());

        if("admin".equals(username)&&"12345".equals(password)) {
            return new UsernamePasswordAuthenticationToken(username,password, Arrays.asList());
        }else {
            throw new AuthenticationCredentialsNotFoundException("not valid password");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

It’s a normal class which implements an interface from spring security.

Let’s check the source code of the class that utilize the CustomAuthentionProvider:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomAuthentionProvider customAuthentionProvider;

    ...
}

You can see that the WebSecurityConfig class injected the class CustomAuthentionProvider by using @Autowired annotation, but why it does not work?

Solution

When we encounter the org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type xxxx available , we should consider whether there is a spring bean named xxx exists in the runtime.

What is a Spring bean?(quote from by baeldung)

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.

Spring Framework provides three ways to configure beans to be used in the application.

  1. Annotation Based Configuration – By using @Service or @Component annotations. Scope details can be provided with @Scope annotation.
  2. XML Based Configuration – By creating Spring Configuration XML file to configure the beans. If you are using Spring MVC framework, the xml based configuration can be loaded automatically by writing some boiler plate code in web.xml file.
  3. Java Based Configuration – Starting from Spring 3.0, we can configure Spring beans using java programs. Some important annotations used for java based configuration are @Configuration, @ComponentScan and @Bean

Now we can choose the first Annotation Based Configuration,which is the most convenient way to define or create spring beans.

Look at the code:

@Component
public class CustomAuthentionProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = String.valueOf(authentication.getCredentials());

        if("bswen".equals(username)&&"12345".equals(password)) {
            return new UsernamePasswordAuthenticationToken(username,password, Arrays.asList());
        }else {
            throw new AuthenticationCredentialsNotFoundException("not valid password");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

The most important line of the codes is:

@Component
public class CustomAuthentionProvider implements AuthenticationProvider {
...
}

The @Component create a bean named CustomAuthentionProvider in spring context.

Rerun the code, the error disappeared.

It works!

All the example code and config files can be found in this github project.