Spring Auto-detection autowire & Component-scanning Example With Annotations

In this post we will see how Spring can auto-detect the beans configured in your application using component-scanning, wire them wherever required without even declaring as @Bean methods in @Configuration class ( or in XML terms, without declaring bean in Spring XML Configuration file). We will also see corresponding XML configuration side-by-side for comparison.

We will proceed with help of a typical enterprise level application setup example showing different layers(Service, DAO) involved in application development. Let’s get going.


Following technologies being used:

  • Spring 4.0.6.RELEASE
  • Joda-time 2.3
  • Maven 3
  • JDK 1.6
  • Eclipse JUNO Service Release 2

Project directory structure

Following will be the final project directory structure for this example:

Let’s add the content mentioned in above directory structure.

Step 1: Provide Spring dependencies in Maven 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.websystique.spring</groupId>
  <artifactId>Spring4AutoScanning</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <name>Spring4AutoScanning</name>
   	<properties>
		<springframework.version>4.0.6.RELEASE</springframework.version>
		<joda-time.version>2.3</joda-time.version>
	</properties>

	<dependencies>
		<!--  Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${springframework.version}</version>
		</dependency>

      	<!-- Joda-Time -->		
		<dependency>
  			<groupId>joda-time</groupId>
  			<artifactId>joda-time</artifactId>
  			<version>${joda-time.version}</version>
  		</dependency>

  	</dependencies>
	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.2</version>
					<configuration>
						<source>1.6</source>
						<target>1.6</target>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>

</project>

We need Spring core and Spring context dependencies to work with this example. Additionally in our example we will be doing some date calculation using JodaTime LocalDate class, so joda-time dependency is in there.

Step 2: Create Spring Configuration Class

Spring configuration class are the ones annotated with @Configuration. These classes contains methods annotated with @Bean. These @Bean annotated methods generates beans managed by Spring container.


package com.websystique.spring.configuration;

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

@Configuration
@ComponentScan(basePackages = "com.websystique.spring")
public class AppConfig {

}

You might have noticed that above class is Empty, no @Bean methods, acting as a placeholder instead. So where will the beans come from?

Actually, they will be auto-detected, thanks to @ComponentScan annotation.

@ComponentScan(basePackages = "com.websystique.spring")

@ComponentScan basePackages attribute takes package name[s] as input which will be search for to find any class annotated with Spring specific annotations.

Below are commonly used Spring annotation which makes a bean auto-detectable:

@Repository – Used to mark a bean as DAO Component on persistence layer
@Service – Used to mark a bean as Service Component on business layer
@Controller – Used to mark a bean as Controller Component on Presentation layer
@Configuration – Used to mark a bean as Configuration Component.
@Component – General purpose annotation, can be used as a replacement for above annotations.

Note that all above mentioned annotations are internally annotated with @Component, so indeed you can use @Component everywhere, but to keep the design & intention clean, it’s advisable to use different annotation based on different condition.

Remark: In our example, you can even discard the Configuration class altogether as it does not contain any @Bean method. We will see how to scan the beans in that case below.

Same Configuration in XML terms can be expressed as below (let’s name it app-config.xml)

<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-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
	<context:component-scan base-package="com.websystique.spring" />
 
</beans>

Step 3: Create DAO Layer Classes


package com.websystique.spring.dao;

import com.websystique.spring.model.Employee;

public interface EmployeeDao {

	void saveInDatabase(Employee employee);
}

package com.websystique.spring.dao;

import org.springframework.stereotype.Repository;

import com.websystique.spring.model.Employee;

@Repository("employeeDao")
public class EmployeeDaoImpl implements EmployeeDao{

	public void saveInDatabase(Employee employee) {

		/*
		 * Logic to save in DB goes here
		 */
		System.out.println("Employee "+employee.getName()+" is registered for assessment on "+ employee.getAssessmentDate());
		
	}

}

@Repository annotation marks this class as an Auto-detectable bean on persistence layer. Parameter to this annotation employeeDao provides a name to this bean. We will inject this class into main service bean.

Step 4: Create Service Layer Classes


package com.websystique.spring.service;

import org.joda.time.LocalDate;

public interface DateService {

	LocalDate getNextAssessmentDate();
}



package com.websystique.spring.service;

import org.joda.time.LocalDate;
import org.springframework.stereotype.Service;

@Service("dateService")
public class DateServiceImpl implements DateService{

	public LocalDate getNextAssessmentDate() {
		return new LocalDate(2015,10,10);
	}

}

@Service marks this class as Auto-detectable bean on business layer. We will inject this class into main service bean.


package com.websystique.spring.service;

import com.websystique.spring.model.Employee;

public interface EmployeeService {

	void registerEmployee(Employee employee);
}


package com.websystique.spring.service;

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

import com.websystique.spring.dao.EmployeeDao;
import com.websystique.spring.model.Employee;

@Service("employeeService")
public class EmployeeServiceImpl implements EmployeeService{

	@Autowired
	private DateService dateService;
	
	@Autowired
	private EmployeeDao employeeDao;
	

	public void registerEmployee(Employee employee) {
		employee.setAssessmentDate(dateService.getNextAssessmentDate());
		employeeDao.saveInDatabase(employee);
	}
	
}

EmployeeService is our main service class.Notice that we have injected both DateService and EmployeeDao in this.
@Autowired on dateService property marks the DateService to be auto-wired by Spring’s dependency injection with the appropriate bean in Spring context. In our case, we have already declared a DateService bean using @Service, so that bean will be injected here. Similarly, employeeDao will be injected by EmployeeDao annotated with @Repository.

Below is the model class Employee used in our example


package com.websystique.spring.model;

import org.joda.time.LocalDate;

public class Employee {

	private int id;

	private String name;

	private LocalDate assessmentDate;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public LocalDate getAssessmentDate() {
		return assessmentDate;
	}

	public void setAssessmentDate(LocalDate assessmentDate) {
		this.assessmentDate = assessmentDate;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", assessmentDate="
				+ assessmentDate + "]";
	}

}

Step 5: Create Main to run as Java Application


package com.websystique.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

import com.websystique.spring.configuration.AppConfig;
import com.websystique.spring.model.Employee;
import com.websystique.spring.service.EmployeeService;

public class AppMain {

	public static void main(String args[]){
		AbstractApplicationContext  context = new AnnotationConfigApplicationContext(AppConfig.class);
		
		EmployeeService service = (EmployeeService) context.getBean("employeeService");

		/*
		 * Register employee using service
		 */
		Employee employee = new Employee();
		employee.setName("Danny Theys");
		service.registerEmployee(employee);
		
		context.close();
	}
}

Run above program as Java Application, you should see following output

Employee Danny Theys is registered for assessment on 2015-10-10

Now, in case you decided to discard the configuration class AppConfig you can do so with following changes

package com.websystique.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.websystique.spring.model.Employee;
import com.websystique.spring.service.EmployeeService;

public class AppMain {

	public static void main(String args[]){
		//AbstractApplicationContext  context = new AnnotationConfigApplicationContext(AppConfig.class);
		AnnotationConfigApplicationContext  context = new AnnotationConfigApplicationContext();
		context.scan("com.websystique.spring"); 
		context.refresh();

		EmployeeService service = (EmployeeService) context.getBean("employeeService");

		/*
		 * Register employee using service
		 */
		Employee employee = new Employee();
		employee.setName("Danny Theys");
		service.registerEmployee(employee);
		
		context.close();
	}
}

AnnotationConfigApplicationContext.scan method performs a scan of all class in specified package[s], registering all the beans annotated with @Component (even @configuration itself is internally annotated with @component) in application context. A caveat here is that refresh must be called after scan in order to fully process the registered classes.

Run above program as Java Application, you should see same output as before.

That’s it.

For XML based configuration , replace

	AbstractApplicationContext  context = new AnnotationConfigApplicationContext(AppConfig.class);

with

AbstractApplicationContext context = new ClassPathXmlApplicationContext("app-config.xml");

in above main, no other changes. Run the program and you will see same output.

Download Source Code


References