Spring Scanning Components

0

Spring’s component scanning feature automatically scans and detects the components from the classpath.

To mark a class as a spring component, all you need to do is annotate the class with @Component.

You also need to provide <context:component-scan> element in your context XML. This will let spring know that it needs to scan certain packages to detect the components.

In this article, we will see some examples of @Component and the specialized annotations @Repository, @Service and Controller.

This example uses the following tools and frameworks:

  1. Java 8
  2. Maven 3.2.3
  3. Spring 4.1.7.RELEASE
  4. Eclipse  as the IDE, version Luna 4.4.1.

Dependencies

Add the following dependencies:

  1. spring-core
  2. spring-context
  3. spring-beans

pom.xml:

<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>com.javarticles.spring</groupId>
	<artifactId>springListExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>
		<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>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${spring.version}</version>
		</dependency>
	</dependencies>

	<properties>
		<spring.version>4.1.6.RELEASE</spring.version>
	</properties>

</project>

Examples of @Component

@Component represents a generic stereotype for any Spring-managed component. If a class is annotated with @Component, spring can scan and detect it while building the context object.

Let’s see an example.

Class Company is annotated with @Component and the bean ID is provided as the value.

If you don’t provide any ID, spring will still load the component. It will assign the bean a name created by lower-casing the first character of the class and using the rest of the camel-cased name for the bean name. You can also provide your own custom bean ID resolving strategy.

Company:

package com.javarticles.spring.components;

import org.springframework.stereotype.Component;

@Component("dreamCompany")
public class Company {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public String toString() {
        return "Company Bean";
    }    
}

Note we have used @Autowired annotation so that Spring can auto-wire it by type.

Employee:

package com.javarticles.spring.components;

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

@Component
@Scope("prototype")
public class Employee {    
    private String name;
    @Autowired
    private Company company;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Employee Bean, " + company;
    }
}

To allow spring to scan the classes, we need to provide a XML element <context:component-scan>. In this element, you need to specify the package for scanning your components using attribute base-package. Then the specified package and all its sub-packages will be scanned. If more than one packages, we need provide them comma separated.

applicationContext.xml:

<?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-2.5.xsd">

	<context:component-scan
		base-package="com.javarticles.spring.components" />

</beans>

SpringComponentScanningExample:

package com.javarticles.spring.components;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringComponentScanningExample {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = null;
        try {
            context = new ClassPathXmlApplicationContext("applicationContext.xml");
            System.out.println(context.getBean("dreamCompany"));
            System.out.println(context.getBean("employee"));
        } finally {
            if (context != null) {
                context.close();
            }
        }
    }
}

Output:

Company Bean
Employee Bean, Company Bean

Disable auto-wiring and the common annotations

By default AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor are both included implicitly when using the <component-scan> element.

AutowiredAnnotationBeanPostProcessor takes care of auto-wiring whereas CommonAnnotationBeanPostProcessor takes care of the common lifecycle annotations @PostConstruct and @PreDestroy.

If a method is annotated with @PostConstruct then it will be called during post bean initialization phase. If the method is annotated with @PreDestory, it will be called before the bean is going to be removed from the container.

ClassEmployeeTax has a @PostConstruct and @PreDestroy method. Since by default, annotation-config is true, the above mentioned annotation processors will automatically be registered.

EmployeeTax:

package com.javarticles.spring.components;

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

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class EmployeeTax {

    @Autowired
    private Employee employee;
    
    @PostConstruct
    public void init() {
      System.out.println("init EmployeeTax bean");      
    }
    
    @PreDestroy
    public void cleanup() {
        System.out.println("cleanup EmployeeTax bean");  
    }

    public Employee getEmployee() {
        return employee;
    }

}

annotationsScanContext.xml:

<?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-2.5.xsd">

	<context:component-scan
		base-package="com.javarticles.spring.components" />

</beans>

If you load the above context, you will notice spring automatically auto-wires the employee bean and calls the lifecycle callbacks @PostConstruct and @PreDestroy annotated methods.

SpringDisableAnnotationsExample:

package com.javarticles.spring.components;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDisableAnnotationsExample {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = null;
        System.out.println("Load context file with default annotation-config=true");
        try {
            context = new ClassPathXmlApplicationContext("annotationsScanContext.xml");
        } finally {
            if (context != null) {
                context.close();
            }
        }
    }
}

context.close() will shutdown the context so it will automatically call the registered @PreDestroy callback methods.

Output:

Load context file with default annotation-config=true
init EmployeeTax bean
cleanup EmployeeTax bean

Let’s now disable the default behavior of configuring annotations setting attribute annotations-config to false.

disableAnnotationsScanContext.xml:

<?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-2.5.xsd">

	<context:component-scan
		base-package="com.javarticles.spring.components" annotation-config="false"/>

</beans>

Since annotation-config is set to false, annotations @Autowired, @PostConstruct and @PreDestroy won’t get processed.

SpringDisableAnnotationsExample:

package com.javarticles.spring.components;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDisableAnnotationsExample {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = null;
        System.out.println("Load context file with default annotation-config=true");
        try {
            context = new ClassPathXmlApplicationContext("annotationsScanContext.xml");
        } finally {
            if (context != null) {
                context.close();
            }
        }
        System.out.println("Load context file with default annotation-config=false");
        try {
            context = new ClassPathXmlApplicationContext("disableAnnotationsScanContext.xml");
            EmployeeTax employeeTax = (EmployeeTax) context.getBean("employeeTax");
            System.out.println("is 'employee' autowired? " + (employeeTax.getEmployee() != null));
        } finally {
            if (context != null) {
                context.close();
            }
        }
    }
}

employee object will be null. You can also see that the lifecycle methods init() and cleanup() are not called.

Output:

Load context file with default annotation-config=true
init EmployeeTax bean
cleanup EmployeeTax bean
Load context file with default annotation-config=false
is 'employee' autowired? false

Specialized annotations @Repository, @Service and @Controller

@Repository, @Service and @Controller serve as specializations of @Component. They are meant for more specific use cases. We can use @Repository annotation in the persistence layer, for example on a Data Access Object (DAO).

@Service in service layer and @Controller annotation on a class representing a controller.

If you use a @Component instead of the specialized ones, spring will still go ahead and load them but the ideal way would be to use the specialized ones as it allows spring to do specialized processing based on the chosen annotation.

For example, Spring offers a solution allowing exception translation to be applied transparently through the @Repository annotation which means irrespective of the persistence technology an exception thrown will be translated to a generic Spring’s org.springframework.dao.DataAccessException  DataAccessException hierarchy.

EmployeeDAO:

package com.javarticles.spring.repository;

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

import com.javarticles.spring.components.Employee;

@Repository
public class EmployeeDAO {
    @Autowired
    private Employee employee;

    public Employee findEmployeeByName(String name) {
        employee.setName("name");
        return employee;
    }
    
    public String toString() {
        return "EmployeeDao, " + employee;
    }
}

Let’s add a Service component.

EmployeeManager:

package com.javarticles.spring.services;

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

import com.javarticles.spring.repository.EmployeeDAO;

@Service
public class EmployeeManager {
    @Autowired
    private EmployeeDAO employeeDAO;
    
    public String toString() {
        return "Employee Manager, " + employeeDAO;
    }
}

Now we will add one controller, this should represent the web controller.

Note our example classes are all dummy, as our goal in this article is just to verify that spring loads them in the same way as it does with classes annotated with @Component.

EmployeeDetailsController:

package com.javarticles.spring.controllers;

import org.springframework.stereotype.Controller;

@Controller
public class EmployeeDetailsController {
    public String toString() {
        return "Employee Controller";
    }
    
}

applicationContext.xml:

<?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-2.5.xsd">

	<context:component-scan
		base-package="com.javarticles.spring.components,com.javarticles.spring.controllers,com.javarticles.spring.repository,com.javarticles.spring.services" />

</beans>

SpringComponentScanningExample:

package com.javarticles.spring.components;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringComponentScanningExample {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = null;
        try {
            context = new ClassPathXmlApplicationContext("applicationContext.xml");
            System.out.println(context.getBean("dreamCompany"));
            System.out.println(context.getBean("employee"));
            System.out.println(context.getBean("employeeManager"));
            System.out.println(context.getBean("employeeDAO"));
            System.out.println(context.getBean("employeeDetailsController"));
        } finally {
            if (context != null) {
                context.close();
            }
        }
    }
}

Output:

Company Bean
Employee Bean, Company Bean
Employee Manager, EmployeeDao, Employee Bean, Company Bean
EmployeeDao, Employee Bean, Company Bean
Employee Controller

Download the source code

This was an example about spring component scanning.

You can download the source code here: springComponentScanExample.zip
Share.

Comments are closed.