Sunday, March 13, 2022

Spring Component Scan Example - Auto Detect Bean and AutoWiring

Though there are ways to explicitly wire dependencies by using Spring XML configuration or Spring JavaConfig but Spring also provides automatic configuration where beans are detected automatically using component scanning and dependencies are wired automatically using Autowiring. In this post we'll see a Spring example program using component scan for auto detection of beans and autowiring of dependencies.

Two things you need to know for automatic configuration in Spring are-

  • Component scanning- Spring does the work of automatically discovering the beans to be created in the application context rather than you explicitly defining them. It is done using the annotation @Component (and some other variants of it like @Service, @Repository, @Controller or any custom annotation that itself is annotated with @Component).
  • Autowiring- Spring does the work of automatically detecting and wiring the dependencies.

In this tutorial Maven is used to create a general Java project structure.

Technologies used

  • Spring 5.0.4
  • JDK 1.8
  • Apache Maven 3.3.3
  • Eclipse IDE

Spring @ComponentScan annotation example step by step

  1. Provide dependencies in pom.xml of Maven

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>org.netjs.exp</groupId>
      <artifactId>Spring-Example</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>
    
      <name>Spring-Example</name>
      <url>http://maven.apache.org</url>
      <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>5.0.4.RELEASE</spring.version>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
         <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>     
      </dependencies>
    </project>
    

    Note that you'll already have most of the entries in pom.xml, you just need to add dependencies for spring-core and spring-context and an entry for spring.version in properties element. Spring-context is needed for the annotations.

  2. Spring @ComponentScan annotation example - Java classes

    We need 2 classes PaymentService and CashPayment. Since Spring promotes loose coupling so it is always recommended to code to interfaces. So we'll also create interfaces for the classes.

    Payment Service

    public interface IPayService {
     void performPayment();
    }
    
    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() {
      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.

    Cash Payment

    public interface IPayment {
     void executePayment();
    }
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class CashPayment implements IPayment{
     public void executePayment() {
      System.out.println("Perform Cash Payment - ");
     }
    }
    
  3. Spring Configuration for component scanning

    For Spring component scanning configuration we can use either a Java class or XML configuration. Both the configurations are shown here.

    If you are using XML configuration then you need to use context:component-scan tag 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. Spring framework will automatically scan that package for components.

    <?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.netjs.prog" />
    </beans>
    

    Notice the addition of new name space.

    xmlns:context="http://www.springframework.org/schema/context"
    

    This is required because we are using context:component-scan.

    If you are using Spring Java configuration then you just need this-

    @Configuration
    @ComponentScan(basePackages="org.netjs.prog")
    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.

  4. Java class to run the application

    If you are using XML configuration then the Java class to run the application will be like this -

    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 appcontext.xml is the name of the XML file created above.

    If you are using Java class then the class to run the application will be like this -

    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.

NoUniqueBeanDefinitionException with Spring autowiring

In the above application we had used only one implementation of IPayment, CashPayment so there was no problem in automatically wiring it. Now suppose we have one more implementation of the IPayment interface, Credit Payment. In that case with the current setup you'll get NoUniqueBeanDefinitionException because Spring won't know which Payment class to wire.

For these types of scenarios where ambiguity is there Spring provides an option of using @Qualifier annotation to qualify the bean.

Spring autowiring with @Qualifier annotation to qualify bean

CreditPayment class

@Component
public class CreditPayment implements IPayment {
 public void executePayment() {
  System.out.println("Performing credit payment ");
 }
}

Since we have two classes implementing IPayment interface now; CashPayment and CreditPayment, so we need to make changes in the PayServiceImpl class and use the qualifier annotation to wire the dependency. If we need CreditPayment class to be wired then PayServiceImpl class will look like this.

@Service
public class PayServiceImpl implements IPayService {
  @Autowired
  @Qualifier("creditPayment")
  private IPayment payment;

  public void performPayment() {
    payment.executePayment();
  }
  
  public IPayment getPayment() {
    return payment;
  }

  public void setPayment(IPayment payment) {
    this.payment = payment;
  }
}

Note the Qualifier annotation used with the payment reference.

That's all for this topic Spring Component Scan Example - Auto Detect Bean and AutoWiring. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Component Scan to Automatically Discover Beans
  2. Autowiring in Spring Using @Autowired and @Inject Annotations
  3. Difference Between component-scan And annotation-config in Spring
  4. How to Read Properties File in Spring Framework
  5. Spring Profiles With Examples

You may also like-

  1. How to Inject Null And Empty String Values in Spring
  2. Circular Dependency in Spring Framework
  3. Bean Scopes in Spring With Examples
  4. Spring NamedParameterJdbcTemplate Select Query Example
  5. Java ThreadLocal Class With Examples
  6. Print Odd-Even Numbers Using Threads And wait-notify Java Program
  7. Difference Between CountDownLatch And CyclicBarrier in Java
  8. Java LinkedBlockingQueue With Examples

No comments:

Post a Comment