javaspringdependency-injectionjsr330

How can an application inform the Spring application context where to find a class whose constructor is annotated with @Inject?


How can I fix the following example in order to inform the Spring application context where to find a class Application whose constructor is annotated with @Inject, but without introducing a bean method to ApplicationConfiguration annotated with @Bean that returns an instance of class Application?

public class Application {
    private final A a;

    @Inject
    public Application(A a) {
        this.a = a;
    }

    public A getA() {
        return a;
    }
}

@Configuration
public class ApplicationConfiguration {
    @Bean
    public A getA() {
        return new A();
    }
}

public class A {
}

public class Start {
    public static void main(String[] arguments) {
        final ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
        final Application application = context.getBean(Application.class);
        application.getA();
    }
}

You can review the source code in the AtInject project on GitHub.

When I run class Start, Spring complains that it cannot find class Application:

May 27, 2016 4:49:55 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@7eda2dbb: startup date [Fri May 27 16:49:55 EDT 2016]; root of context hierarchy
May 27, 2016 4:49:55 PM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.opessoftware.atinject.application.Application] is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:371)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:331)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:975)
    at com.opessoftware.atinject.start.Start.main(Start.java:11)

Solution

  • Following M. Deinum's advice, I annotated class Application with stereotype @Component to tell Spring to treat Application as a bean and annotated class ApplicationConfiguration with @ComponentScan in order to direct Spring where to find component Application:

    @Component
    public class Application {
        private final A a;
    
        @Inject
        public Application(A a) {
            this.a = a;
        }
    
        public A getA() {
            return a;
        }
    }
    
    @Configuration
    @ComponentScan({"me.derekmahar.atinject.application", "me.derekmahar.atinject.model"})
    public class ApplicationConfiguration {
        @Bean
        public A getA() {
            return new A("A1");
        }
    }
    

    Note that I also modified class A so that it accepts a name:

    public class A {
        private final String name;
    
        public A(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    }
    

    Class Start now correctly prints "Name of a1 is "A1".":

    May 30, 2016 11:14:49 AM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
    INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@7eda2dbb: startup date [Mon May 30 11:14:49 EDT 2016]; root of context hierarchy
    May 30, 2016 11:14:49 AM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
    INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
    Name of a1 is "A1".
    

    You can find the source code to the solution in the AtInject project on GitHub.