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


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

If you like tutorials on this site, why not take a step further and connect me on Facebook , Google Plus & Twitter as well? I would love to hear your thoughts on these articles, it will help me improve further our learning process.

If you appreciate the effort I have put in this learning site, help me improve the visibility of this site towards global audience by sharing and linking this site from within and beyond your network. You & your friends can always link my site from your site on www.websystique.com, and share the learning.

After all, we are here to learn together, aren’t we?

  • Peadar

    Why do you do all of that and then use getBean()?

    • websystique

      Hi Peadar, getBean is only used inside Main, & that too because i am handling application context manually.

      • Peadar

        It’s better practice to Autowire the bean so you don’t have to call getBean() on it. Look into SpringBeanAutowiringSupport

        • websystique

          Thanks for Your comment. However, as i mentioned, this is the Main class [not a spring component, no @Component or other annotations is used here, although we may use it, but in this example, we are not], and thus i am getting the context manually, and eventually get the Bean. With Spring components, i do use @Autowired as in this post.