0

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)
Derek Mahar
  • 27,608
  • 43
  • 124
  • 174
  • you haven't declared class `Application` as Bean, which means spring knows nothing about `Application`, how could it be found from `ApplicationContext` – Jaiwo99 May 27 '16 at 21:40
  • I understand your point, but it's not possible to annotate a *class* with `@Bean`, only a method in a `@Configuration` class that returns an instance of `Application`. I think it is possible for Spring to find constructors that are annotated with `@Inject`, but I'm not sure how to instruct Spring how and where to find them. – Derek Mahar May 27 '16 at 23:13
  • you can scan for annotation, then create the bean manually, but that's the point of doing that.. – Jaiwo99 May 28 '16 at 07:30
  • @Jaiwo99: For example? – Derek Mahar May 28 '16 at 08:23
  • 1
    You would have to put `@Component` (or one of the specific annotations) on he `Application` class for it to be detected. You will have to tell spring at least where to look and what to instantiate, it won't auto magically create beans (nor will CDI because you need to have `@Named` on it). – M. Deinum May 28 '16 at 10:19
  • @M. Deinum: I will try your `@Component` suggestion. Shouldn't `context.getBean(Application.class)` be a sufficient hint to Spring about what to instantiate? – Derek Mahar May 28 '16 at 12:28
  • 1
    No it isn't. That means you want a bean of type `Application` from the context, but if there isn't anything in the context defining that bean it will throw an error. – M. Deinum May 29 '16 at 08:41

1 Answers1

1

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.

Community
  • 1
  • 1
Derek Mahar
  • 27,608
  • 43
  • 124
  • 174