April 11, 2022

Spring Component Scanning Example

In Spring XML configuration example we saw how configuration metadata is specified in XML that is used to register beans and wire dependencies. Spring framework also provides an option to automatically discover beans and automatically wire dependencies, in this Spring component scanning example we’ll see how that is done.

Spring Component scan

By using component scan Spring framework automatically detects the bean classes by scanning the classpath and registers the bean definition with the container. This eliminates the need to use XML to perform bean registration and keeps the XML to bare minimum (or you can use Java configuration).

For implicit detection of beans you can use @Component annotation on the classes for which you want bean definitions registered with the container. Note that @Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component for more specific use cases.

  • @Repository- Persistence layer classes
  • @Service- Service layer classes
  • @Controller- Presentation layer classes.

Automatic discovery of bean and automatic wiring

For automatic configuration in Spring you need to take care of two things-

  1. Component scanning- As already explained by using component scanning automatic detection of beans and registering the bean definition with the application context is done by the Spring framework. When using XML configuration you just need to use <context:component-scan> element along with the base-package to provide the packages that are to be scanned for components. In Spring Java based configuration you can use @ComponentScan annotation for enabling component scanning.
  2. Automatic wiring- Automatic injection of dependencies, for that @Autowired annotation is used.

Spring Component scan example

In the example there are two discoverable beans EmployeeService and EmployeeDAO. With EmployeeService class @Service annotation is used where as @Repository annotation is used with EmployeeDAO class. Use of these annotations make these classes eligible for scanning when component scanning is done.

public class Employee {
  int empId;
  String empName;
  String dept;
  public int getEmpId() {
    return empId;
  }
  public void setEmpId(int empId) {
    this.empId = empId;
  }
  public String getEmpName() {
    return empName;
  }
  public void setEmpName(String empName) {
    this.empName = empName;
  }
  public String getDept() {
    return dept;
  }
  public void setDept(String dept) {
    this.dept = dept;
  }

  @Override
  public String toString() {
    return "Id= " + getEmpId() + " Name= " + 
             getEmpName() + " Dept= "+ getDept();
  }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.knpcode.springproject.dao.EmployeeDAO;
import com.knpcode.springproject.model.Employee;

@Service
public class EmployeeService {
  // Dependency
  @Autowired
  EmployeeDAO employeeDAO;

  public Employee getEmployee() {
    return employeeDAO.findEmployee();
  }
}
import org.springframework.stereotype.Repository;
import com.knpcode.springproject.model.Employee;

@Repository
public class EmployeeDAO {
  public Employee findEmployee() {
    return getEmployee();
  }
	
  // Stub method for getting Employee info
  private Employee getEmployee() {
    Employee emp = new Employee();
    emp.setEmpId(1);
    emp.setEmpName("Joe Marshal");
    emp.setDept("Finance");
    return emp;
  }
}

EmployeeService class has a dependency on EmployeeDAO which is satisfied automatically that is why @Autowired annotation is used with the employeeDAO property.

Component scanning using <context:component-scan/> element - 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: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="com.knpcode.springproject.service,
						     com.knpcode.springproject.dao"/>
</beans>

Using base-package attribute, packages where the bean classes resides are specified as a comma separated list.

You can use the following class with main method to read the configuration and call the bean method. By default the Id given to a bean is derived from the class by lowercasing the first letter of the class name so bean ID for EmployeeService class is employeeService.

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.knpcode.springproject.model.Employee;
import com.knpcode.springproject.service.EmployeeService;

public class App {
  public static void main( String[] args ){
    // create context using configuration
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
    EmployeeService employeeService = context.getBean("employeeService", EmployeeService.class);
    Employee emp = employeeService.getEmployee();
    System.out.println("Employee- " + emp);
    // close the context
    context.close();
  }
}
Output
11:39:00.431 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 6 bean definitions from class path resource [appcontext.xml]
11:39:00.481 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
11:39:00.657 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
11:39:00.666 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
11:39:00.674 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
11:39:00.698 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'employeeService'
11:39:00.873 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'employeeDAO'
Employee- Id= 1 Name= Joe Marshal Dept= Finance

Using Spring @ComponentScan annotation for enabling component scanning

If you want to use java configuration then you need the following class.

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

@Configuration
@ComponentScan(basePackages= {"com.knpcode.springproject.service",
                             "com.knpcode.springproject.dao"})
public class AppConfig {

}

For running the application you can use the following class.

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import com.knpcode.springproject.model.Employee;
import com.knpcode.springproject.service.EmployeeService;

public class App {
  public static void main( String[] args ){
    AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    EmployeeService employeeService =  context.getBean("employeeService", EmployeeService.class);
    Employee emp = employeeService.getEmployee();
    System.out.println("Employee- " + emp);
    context.close();
  }
}

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

Using filters to customize component scanning

By applying exclusion and inclusion filters you can specify which components are detected. You can add them as includeFilters or excludeFilters parameters of the @ComponentScan annotation when using Java config or as include-filter or exclude-filter child elements of the component-scan element when using XML.

For example if you want to include classes in Service package and want to exclude classes in DAO package from component scanning.

With @ComponentScan

@ComponentScan(basePackages= {"com.knpcode.springproject.service",
                             "com.knpcode.springproject.dao"}, includeFilters = @Filter(type = FilterType.REGEX, pattern ="com.knpcode.springproject.service..*"),
                             excludeFilters = @Filter(type = FilterType.REGEX,  pattern ="com.knpcode.springproject.dao..*"))
With <context:component-scan />
<context:component-scan base-package="com.knpcode.springproject.service,
				   com.knpcode.springproject.dao">
	<context:include-filter type="regex" expression="com\\.knpcode\\.springproject\\.service\\..*"/>
	<context:exclude-filter type="regex" expression="com.knpcode.springproject.dao..*"/>
</context:component-scan>

That's all for the topic Spring Component Scanning Example. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment