Friday 23 September 2016

Spring MVC: context:annotation-config vs context:component-scan

1) First big difference between both tags is that<context:annotation-config> is used to activate applied annotations in already registered beans in application context. Note that it simply does not matter whether bean was registered by which mechanism e.g. using <context:component-scan> or it was defined in application-context.xml file itself.
2) Second difference is driven from first difference itself. It does register the beans in context + it also scans the annotations inside beans and activate them. So <context:component-scan>; does what <context:annotation-config>does, but additionally it scan the packages and register the beans in application context.

Example of <context:annotation-config> vs <context:component-scan> uses

I will elaborate both tags in detail with some examples which will make more sense to us. For keeping the example to simple, I am creating just 3 beans, and I will try to configure them in configuration file in various ways, then we will see the difference between various configurations in console where output will get printed.
For reference, below are 3 beans. BeanA has reference to BeanB and BeanC additionally.
package com.howtodoinjava.beans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@SuppressWarnings("unused")
@Component
public class BeanA {
     
    private BeanB beanB;
    private BeanC beanC;
     
    public BeanA(){
        System.out.println("Creating bean BeanA");
    }
    @Autowired
    public void setBeanB(BeanB beanB) {
        System.out.println("Setting bean reference for BeanB");
        this.beanB = beanB;
    }
    @Autowired
    public void setBeanC(BeanC beanC) {
        System.out.println("Setting bean reference for BeanC");
        this.beanC = beanC;
    }
}
//Bean B
package com.howtodoinjava.beans;
import org.springframework.stereotype.Component;
@Component
public class BeanB {
    public BeanB(){
        System.out.println("Creating bean BeanB");
    }
}
//Bean C
package com.howtodoinjava.beans;
import org.springframework.stereotype.Component;
@Component
public class BeanC {
    public BeanC(){
        System.out.println("Creating bean BeanC");
    }
}
BeanDemo class is used to load and initialize the application context.
package com.howtodoinjava.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanDemo {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml");
    }
}
Now let’s start writing the configuration file "beans.xml" with variations. I will be omitting the schema declarations in below examples, to keep focus on configuration itself.

a) Define only bean tags

<bean id="beanA" class="com.howtodoinjava.beans.BeanA"></bean>
<bean id="beanB" class="com.howtodoinjava.beans.BeanB"></bean>
<bean id="beanC" class="com.howtodoinjava.beans.BeanC"></bean>
Output:
Creating bean BeanA
Creating bean BeanB
Creating bean BeanC
In this case, all 3 beans are created and no dependency in injected in BeanA because we didn’t used any property/ref attributes.

b) Define bean tags and property ref attributes

<bean id="beanA" class="com.howtodoinjava.beans.BeanA">
    <property name="beanB" ref="beanB"></property>
    <property name="beanC" ref="beanC"></property>
</bean>
<bean id="beanB" class="com.howtodoinjava.beans.BeanB"></bean>
<bean id="beanC" class="com.howtodoinjava.beans.BeanC"></bean>
Output:
Creating bean BeanA
Creating bean BeanB
Creating bean BeanC
Setting bean reference for BeanB
Setting bean reference for BeanC
Now the beans are created and injected as well. No wonder.

c) Using only <context:annotation-config />

<context:annotation-config />
//No Output
As I told already, <context:annotation-config /> activate the annotations only on beans which have already been discovered and registered. Here, we have not discovered any bean so nothing happened.

d) Using <context:annotation-config /> with bean declarations

<context:annotation-config />
<bean id="beanA" class="com.howtodoinjava.beans.BeanA"></bean>
<bean id="beanB" class="com.howtodoinjava.beans.BeanB"></bean>
<bean id="beanC" class="com.howtodoinjava.beans.BeanC"></bean>
Output:
Creating bean BeanA
Creating bean BeanB
Setting bean reference for BeanB
Creating bean BeanC
Setting bean reference for BeanC
In above configuration, we have discovered the beans using <bean> tags. Now when we use <context:annotation-config />, it simply activates @Autowired annotation and bean injection inside BeanA happens.

e) Using only <context:component-scan />

<context:component-scan base-package="com.howtodoinjava.beans" />
Output:
Creating bean BeanA
Creating bean BeanB
Setting bean reference for BeanB
Creating bean BeanC
Setting bean reference for BeanC
Above configuration does both things as I mentioned earlier in start of post. It does the bean discovery (searches for@Component annotation in base package) and then activates the additional annotations (e.g. Autowired).

f) Using both <context:component-scan /> and <context:annotation-config />

<context:annotation-config />
<context:component-scan base-package="com.howtodoinjava.beans" />
<bean id="beanA" class="com.howtodoinjava.beans.BeanA"></bean>
<bean id="beanB" class="com.howtodoinjava.beans.BeanB"></bean>
<bean id="beanC" class="com.howtodoinjava.beans.BeanC"></bean>
Output:
Creating bean BeanA
Creating bean BeanB
Setting bean reference for BeanB
Creating bean BeanC
Setting bean reference for BeanC
Strange !! With above configuration we are discovering beans two times and activating annotations two times as well. But output got printed one time only. Why? Because spring is intelligent enough to register any configuration processing only once if it is registered multiple tiles using same or different ways. Cool !!