Sunday, January 3, 2021

Spring Component Scan to Automatically Discover Beans

In the post how to autowire using XML config and how to autowire using annotation you have already seen how Spring can automate the injection of dependencies. That helps in cutting down the configuration you have to do by eliminating the use of <property> and <constructor-arg> tags for wiring the bean references. But Spring can even automatically discover beans using <context:component-scan> element. In this post we'll see how to configure and use component scanning in Spring.


Automatic discovery of beans in Spring

Spring goes even further than autowiring and it can automatically discover beans for you. That can be done using <context:component-scan> element or @ComponentScan annotation, it configures Spring to automatically discover beans and declare them for you. This removes the need to use XML to perform bean registration and for most of the classes you don’t need <bean> element to declare and wire beans.

Note here that - The use of <context:component-scan> implicitly enables the functionality of <context:annotation-config>. There is usually no need to include the <context:annotation-config> element when using <context:component-scan>. So <context:component-scan> will do everything which <context:annotation-config> does and it provides the added functionality of auto-discovery of beans. In fact difference between <context:annotation-config> and <context:component-scan> is one of the frequently asked interview question.

Spring component scan configuration requirement

If you are using XML configuration then you need to use context:component-scan with base-package. The base-package attribute tells <context:component-scan> from which package it needs to start its component scan.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="org.example"/>
</beans>

If you are using Spring Java configuration then you need to add @ComponentScan annotation along with @Configuration annotation-

@Configuration
@ComponentScan(basePackages="org.example")
public class AppConfig {
    
}

You can annotate a Java class with @ComponentScan and provide the package which is to be scanned with basePackage attribute. You don't need to provide any code with in the class.

Annotations used in Spring for auto-discovery

Using componentscan element is half of the picture, you need to annotate your classes which needs to be auto-discovered with one of these annotations @Component, @Service, @Controller, @Repository

  • @Component- It is a generic stereotype for any Spring-managed component.
  • @Service- More suitable for a class used as a service.
  • @Repository- This annotation is a marker for any class that fulfills the role or stereotype of a repository (also known as Data Access Object or DAO).
  • @Controller- More suitable for a class used in presentation layer as a controller.
  • Any custom annotation that is itself annotated with @Component.

According to the Spring reference doc-

"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."

Spring component scanning example

Here we have a class PayServiceImpl which has a field payment of type IPayment which we have to autowire. Also PayServiceImpl and CashPayment (implementing class of interface IPayment) should be auto discovered. Since Spring promotes loose coupling so it is always recommended to code to interfaces. So we'll also create interfaces for the classes.

IPayService Interface

public interface IPayService {
 void performPayment();
}

PayServiceImpl class

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PayServiceImpl implements IPayService {
 @Autowired
 private IPayment payment;

 public void performPayment() {
  // calling method on Ipayment implementing class
  payment.executePayment();
 }
 
 public IPayment getPayment() {
  return payment;
 }
 
 public void setPayment(IPayment payment) {
  this.payment = payment;
 }
}

Here note that the class is annotated with @Service annotation, that's how it is made sure that this class is discovered while a component scan is done. Also note that the dependency to the payment is annotated with the annotation @Autowired. That's how Spring will automatically detect this dependency and inject it.

Interface IPayment

public interface IPayment {
 void executePayment();
}

Class CashPayment

import org.springframework.stereotype.Component;

@Component
public class CashPayment implements IPayment{
 public void executePayment() {
  System.out.println("Perform Cash Payment - "); 
 }
}

Spring component scan XML Configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
 
   <context:component-scan base-package="org.netjs.prog" />  
 
</beans>

If you are using XML configuration then you need to use context:component-scan with base-package. I have created Java classes in the package org.netjs.prog so that's what I'll give as the base package. Please change accordingly.

You can run it using the following program-

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
  public static void main( String[] args ){  
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext
     ("appcontext.xml");
    IPayService bean = (IPayService) context.getBean("payServiceImpl");
    bean.performPayment();
    context.close();
  }
}

Here note that by default Spring will use the class name as the bean id (camel cased) while registering it in Spring. That's why you can find the bean using this- context.getBean("payServiceImpl");

If you want to give your own name let's say payService then it can be done like this-

@Service("payService")
public class PayServiceImpl implements IPayService

Then in order to get the bean you have to use this name -

IPayService bean = (IPayService) context.getBean("payService");

Spring component scan example with Java config

If you are using Java class then you just need this

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages="org.netjs.prog")
public class AppConfig {
 
}

In that case you can run it using the following program-

import org.netjs.config.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

public class App {
  public static void main( String[] args ){
    AbstractApplicationContext context = new AnnotationConfigApplicationContext
     (AppConfig.class);
    IPayService bean = (IPayService) context.getBean("payServiceImpl");
    bean.performPayment();
    context.close();
  }
}

Here note that AnnotationConfigApplicationContext is used to create the Spring application context which takes the JavaConfig class as input.

That's all for this topic Spring Component Scan to Automatically Discover Beans. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Constructor Based Dependency Injection
  2. Spring Java Configuration Example Using @Configuration
  3. Autowiring in Spring Using @Autowired and @Inject Annotations
  4. Autowiring in Spring Using XML Configuration
  5. @Conditional Annotation in Spring

You may also like-

  1. How to Read Properties File in Spring Framework
  2. @Resource Annotation in Spring Autowiring
  3. Circular Dependency in Spring Framework
  4. Java LinkedBlockingQueue With Examples
  5. Java ThreadLocal Class With Examples
  6. Abstraction in Java
  7. How HashMap Works Internally in Java
  8. Print Odd-Even Numbers Using Threads And wait-notify Java Program

No comments:

Post a Comment