Spring java annotation-based config won't work, but xml-based config does?

I've been following along with Chapter 1 of the book "Spring in Action", 4th Edition. The first chapter has a simple example demonstrating Dependency Injection and bean wiring. (https://livebook.manning.com/book/spring-in-action-fourth-edition/chapter-1)

I've copied the code into intelliJ, but had to make my own project structure. I'm having some trouble with Application Contexts. If I use an xml file along with ClassPathXmlApplicationContext, it works fine. However, when I tried using a .java file to declare my beans, and AnnotationConfigApplicationContext, it wouldn't work, giving me the following error message:

org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@41a4555e: startup date [Mon Jan 17 20:22:09 GMT 2022]; root of context hierarchy
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.springinaction.knights.Knight] is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:319)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:987)
    at com.springinaction.knights.KnightMain.main(KnightMain.java:12)

The 2 relevant classes are shown below:

src/main/java/com/springinaction/knights/config/KnightBeans.java :

package com.springinaction.knights.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.springinaction.knights.BraveKnight;
import com.springinaction.knights.Knight;
import com.springinaction.knights.Quest;
import com.springinaction.knights.SlayDragonQuest;

@Configuration
public class KnightBeans
{
   @Bean
   public Knight knight() {
      return new BraveKnight(quest());
   }

   @Bean
   public Quest quest() {
      return new SlayDragonQuest(System.out);
   }

}

src\main\java\com\springinaction\knights\KnightMain.java :

package com.springinaction.knights;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class KnightMain {

   public static void main(String[] args) throws Exception {
      AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(
                  "com.springinaction.knights.config.KnightBeans.class");
      Knight knight = context.getBean(Knight.class);
      knight.embarkOnQuest();
      context.close();
   }

}

Additional info: I have created interfaces for Knight and Quest, which are implemented by the BraveKnight and SlayDragonQuest classes. All of these, as well as KnightMain.java, reside in the "knights" package. KnightBeans.java resides in the subpackage called config.

I've tried putting various classpaths in new AnnotationConfigApplicationContext(), but always get the same error.

Can anyone explain why this is?


According to the javadoc for AnnotationConfigApplicationContext, the constructor that takes a string argument expects the name of a package to scan for configuration files. You can alternatively pass one or more class objects or a factory.

In your case, Spring will be expecting to find a package called com.springinaction.knights.config.KnightBeans.class. Instead, you probably wanted to pass the class itself, without the double quotes:

AnnotationConfigApplicationContext context =
    new AnnotationConfigApplicationContext(com.springinaction.knights.config.KnightBeans.class);

You could also do

AnnotationConfigApplicationContext context =
    new AnnotationConfigApplicationContext("com.springinaction.knights.config");

to scan all the configs in the config package.


When you are fetching the bean using context.getBean() in that you need to pass the method name of the bean so that spring gets to know what is the bean needs to inject.

package com.springinaction.knights;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class KnightMain {

public static void main(String[] args) throws Exception {
  AnnotationConfigApplicationContext context =
        new AnnotationConfigApplicationContext(
              "full class path");
  Knight knight = context.getBean("knight",Knight.class);
  knight.embarkOnQuest();
  context.close();
}

}

And in AnnotationConfigApplicationContext