Having Spring's IoC container instantiate beans with zero config (like Google Guice's behaviour)

524 Views Asked by At

I have a hobby-project which I would like to migrate to Spring.

As an example I have the following classes:

public class OtherBean {
    public void printMessage() {
        System.out.println("Message from OtherBean");
    }
}


public class InjectInMe {
    @Inject OtherBean otherBean;

    public void callMethodInOtherBean() {
        otherBean.printMessage();
    }
}

However as I read the documentation, I must annotate all classes to be managed by Spring with an annotation like @Component (or others that are similar).

Running it with the following code:

public class SpringTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.refresh();

        InjectInMe bean = context.getBean(InjectInMe.class);
        bean.callMethodInOtherBean();
    }
}

Gives me the error:

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [somepackage.InjectInMe] 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:968)
    at somepackage.SpringTest.main(SpringTest.java:10)

My question is: is there a way to make Spring manage any class I ask the ApplicationContext to instantiate without me having to register them in an Annotated Config (or XML config)?

In Guice I can just inject into the class

public class GuiceTest {
    static public class GuiceConfig extends AbstractModule {

        @Override
        protected void configure() {}

    }
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new GuiceConfig());
        InjectInMe bean = injector.getInstance(InjectInMe.class);
        bean.callMethodInOtherBean();
    }
}

Gives me the output:

Message from OtherBean

Is there anyway I can make Spring work like Guice? As in make Spring inject my beans without me having to register or scan packages for classes annotated with @Component-like annotations?

Any Spring gurus have a way to solve this problem?

3

There are 3 best solutions below

1
On BEST ANSWER

My question is: is there a way to make Spring manage any class I ask the ApplicationContext to instantiate without me having to register them in an Annotated Config (or XML config)?

No, it's not possible. This is simply not how Spring is designed. You must either implicitly (e.g. scan + annotation) or explicitly (e.g. XML bean definition) register your beans.

The least you can do is something like:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(OtherBean.class);
context.register(InjectInMe.class);
context.refresh();
2
On

There are a few ways to provide bean definitions for Spring's ApplicationContext:

  1. Use component scanning and annotate your types with @Component or one of its meta annotations (@Service, @Controller, etc.)
  2. Define a <bean> in XML configuration or a @Bean in Java configuration (or whatever other type of explicit configuration).
  3. Use a BeanDefinitionRegistryPostProcessor to register beans directly on a BeanDefinitionRegistry.
  4. Some ApplicationContext subtypes implement BeanDefinitionRegistry, so you can register bean definitions with them directly.

The beans generated from these bean definitions will be available when injection is required. You wouldn't want (at least IMO) your container to instantiate a type without you telling it somehow that it is OK.

6
On

You must either declare the class as a @Component, or whatever it may be, or you must provide an xml definition. If you don't define the bean, how is Spring supposed to know what to do? For the properties of a class, e.g. MyController has a private service MyService. If you put the @Autowired tag above the service instantiation, Spring will automatically inject this server into your controller, which is what your comment about @Inject seems to hint at. However, in order to autowire, you must define a bean for that class, which means you must use either the @Component/@Controller, etc annotation configuration or an xml bean configuration.