Close

Spring - JavaConfig with Component Scan

[Last Updated: Mar 24, 2021]

To configure Spring container with our beans, we can mix XML's <context:component-scan> with JavaConfig configuration. We can even avoid XML altogether by using @ComponentScan.

With @ComponentScan we can still mix the factory approach . In factory approach we annotate classes with @Configuration having methods annotated with @Bean which return bean instances).

For component scanning to work we must annotate our @Configuration class with @ComponentScan and annotate our bean classes with one of the stereotype annotations

  1. Component
  2. Controller
  3. Repository
  4. Service

Classes annotated with one of the above are candidate for spring container registration when using scanning. The most important is Component annotation. The rest are specialization of Component. Each one is annotated with Component itself. They represent the roles in the overall application design.

you can annotate your component classes with @Component, but by annotating them with @Repository, @Service, or @Controller instead, your classes are more properly suited for processing by tools or associating with aspects. For example, these stereotype annotations make ideal targets for pointcuts. It is also possible that @Repository, @Service, and @Controller may carry additional semantics in future releases of the Spring Framework. Thus, if you are choosing between using @Component or @Service for your service layer, @Service is clearly the better choice.

Along with above annotations we can tag our beans with any of @Lazy, @DependsOn, @Scope etc to specify their specific behavior.


Following diagram summarizes how @ComponentScan works:



Spring Stereotype Annotations are not Inherited

None of the stereotype annotations are tagged with @Inherited. That means we cannot expect sub classes to inherit the stereotype annotations. We have to add them explicitly to each sub classes. Same is true for other annotations like Scope, Lazy etc. As per general Java concept, we can make use of method/fields/constructor level annotation from super classes, i.e. we don't have to repeat them in subclasses. In case of method overriding (typically setters in Spring), we have to explicitly add annotations in overridden methods, even though they are already present in the original super class methods.

Example

A singleton bean

package com.logicbig.example.bean;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class MySingletonBean {

  @PostConstruct
  public void init() {
      System.out.println("initializing " +
              this.getClass().getSimpleName());
  }
}

Service beans

package com.logicbig.example.bean;

public interface MyService {

  String getMessage();
}
package com.logicbig.example.bean;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;

@Lazy
@Service("basic-service")
public class ServiceImplA implements MyService {

  @PostConstruct
  private void init() {
      System.out.println("initializing lazily " +
              this.getClass().getSimpleName());
  }

  @Override
  public String getMessage() {
      return "Message from " + getClass().getSimpleName();
  }
}
package com.logicbig.example.bean;

import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;

@Service
public class ServiceImplB implements MyService {

  @PostConstruct
  private void init(){
      System.out.println("initializing at start up " +
              this.getClass().getSimpleName());
  }
  @Override
  public String getMessage() {
      return "Message from "+getClass().getSimpleName();
  }
}

A prototype bean

package com.logicbig.example.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MyPrototypeBean {

  @Autowired
  @Qualifier("basic-service")
  private MyService myService;

  public void doSomething(){
      System.out.println(myService.getMessage());
  }
}

@Configuration class with @ComponentScan

package com.logicbig.example.app;

import com.logicbig.example.bean.MyPrototypeBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.logicbig.example.bean")
public class AppConfig {

  public static void main(String... strings) {
      AnnotationConfigApplicationContext context =
              new AnnotationConfigApplicationContext(AppConfig.class);
      System.out.println("-- Spring container started and is ready --");
      MyPrototypeBean bean = context.getBean(MyPrototypeBean.class);
      bean.doSomething();
  }
}

Output

initializing MySingletonBean
initializing at start up ServiceImplB
-- Spring container started and is ready --
initializing lazily ServiceImplA
Message from ServiceImplA

Example Project

In this example we are going to demonstrate component scanning along with various concepts we have learnt so far.

Dependencies and Technologies Used:

  • Spring Context 4.2.3.RELEASE: Spring Context.
  • JDK 1.8
  • Maven 3.0.4

@ComponentScan Example Select All Download
  • component-scanning-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • app
                  • AppConfig.java
                  • bean

    See Also