Autowire using Java Based Spring Configuration

In one of our earlier post, we saw an example of Java based Spring Configuration. In that example, we created a Configuration class and wired the beans manually.

In this example, we’ll autowire the Spring beans using @Autowired and other stereotype annotations and explore the power of auto wiring in Java based Spring Configuration.

Class Structure

The class structure that we are going to create would be as follows:

  • Product – a POJO class to store product info
  • BookDAO and MobileDAO – an interface that we will use in a dependent service class
  • BookDAOImpl and MobileDAOImpl – implementation classes for BookDAO and MobileDAOImpl returns list of mocked product objects
  • ProductService – an interface that we will use in the application to hold a service instance
  • ProductServiceImpl – an implementation of ProductService and the dependent of MobileDAO and BookDAO instances
  • Application – It will get the ProductService instance to fetch products
  • AppConfig – a configuration class replacing the XML-based configuration

Creation of Maven Project

Create a new Maven project by skipping archtype selection in STS. Update the pom.xml file as below to download Spring dependencies.

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.techstackjournal</groupId>
	<artifactId>java-config-autowire</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.2.RELEASE</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.2</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Once above pom.xml file is added to your project, right click on the project, and select Maven=>Update Project. This will download all the required jar files for the application.

Creating the Model Class Product

Let’s create a Product model class which will hold the instances returned by the DAO layer.

package com.techstackjournal.model;

public class Product {

	private String productId;
	private String productName;
	private double price;

	public Product() {

	}

	public Product(String productId, String productName, double price) {
		super();
		this.productId = productId;
		this.productName = productName;
		this.price = price;
	}

	public String getProductId() {
		return productId;
	}

	public void setProductId(String productId) {
		this.productId = productId;
	}

	public String getProductName() {
		return productName;
	}

	public void setProductName(String productName) {
		this.productName = productName;
	}

	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

	@Override
	public String toString() {
		return "Product [productId=" + productId + ", productName=" + productName + ", price=" + price + "]";
	}

}

Data Access Layer

In this layer, we’ll define BookDAO and MobileDAO interfaces and their implementations, respectively. We will not deal with the actual data access code here and instead return a mock object to keep things simple and focused on auto wiring of Java based Spring configuration.

See also  Annotation Based Spring Config and Auto Wiring

Let’s first create BookDAO interfaces.

package com.techstackjournal.dao;

import java.util.List;

import com.techstackjournal.model.Product;

public interface BookDAO {

	List<Product> findAll();

}

Here goes MobileDAO interfaces.

package com.techstackjournal.dao;

import java.util.List;

import com.techstackjournal.model.Product;

public interface MobileDAO {

	List<Product> findAll();

}

The point to note in the below implementation of BookDAO is that that we are annotating it with a stereotype annotation @Repository. This will signify that this class contains data access related code and also allows this class to be auto-detected through class path scanning.

package com.techstackjournal.dao;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Repository;

import com.techstackjournal.model.Product;

@Repository("bookDao")
public class BookDAOImpl implements BookDAO {
	@Override
	public List<Product> findAll() {
		List<Product> products = new ArrayList<Product>();
		products.add(new Product("B1", "Book", 10));
		return products;
	}
}

Similarly, we’ll annotate MobileDAO implementation with @Repository annotation.

package com.techstackjournal.dao;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Repository;

import com.techstackjournal.model.Product;

@Repository("mobileDao")
public class MobileDAOImpl implements MobileDAO {
	public List<Product> findAll() {
		List<Product> products = new ArrayList<Product>();
		products.add(new Product("M1", "Mobile", 400));
		return products;
	}
}

Service Layer

In this service layer, we’ll first create the ProductService interface which provides findAll method that returns a list of products.

package com.techstackjournal.service;

import java.util.List;

import com.techstackjournal.model.Product;

public interface ProductService {

	List<Product> findAll();

}

We’ll annotate the ProductServiceImpl class with @Service annotation showing that this class holds business logic of this application. This annotation also helps Spring Container to auto-detect this class through component scan.

Autowire the Beans at Field Level

We also annotate the fields MobileDAO and BookDAO with @Autowired annotation, instructing Spring Container to inject these dependencies at field level.

package com.techstackjournal.service;

import java.util.List;

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

import com.techstackjournal.dao.BookDAO;
import com.techstackjournal.dao.MobileDAO;
import com.techstackjournal.model.Product;

@Service
public class ProductServiceImpl implements ProductService {

	@Autowired
	private MobileDAO mobileDao;

	@Autowired
	private BookDAO bookDao;

	public List<Product> findAll() {
		List<Product> productList = mobileDao.findAll();
		productList.addAll(bookDao.findAll());
		return productList;
	}

}

Once we have the dependencies of MobileDAO and BookDAO, within findAll method, we call their corresponding findAll method and consolidate the list of products before returning to the caller function.

See also  Autowire Spring Beans using XML Configuration

Application Configuration Class

Annotation @Configuration indicates that this class is an application configuration class, a replacement of the XML-based configuration. We annotate this class with @ComponentScan annotation, that instructs Spring Container to look for the Spring beans into specified list of packages. Spring Container would scan through all these packages for the classes that are annotated with stereotypes like @Repository, @Service or @Component etc., and create instances of those classes.

Since there is no explicit need of instantiating the beans BookDAOImpl and MobileDAOImpl, the application configuration class will be empty.

package com.techstackjournal.app;

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

@ComponentScan({ "com.techstackjournal" })
@Configuration
public class AppConfig {

}

Application Class

This is the class that contains main method that gets the instance of ProductService from AnnotationConfigApplicationContext class and prints all the retrieved products.

package com.techstackjournal.app;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.techstackjournal.model.Product;
import com.techstackjournal.service.ProductService;

public class Application {

	public static void main(String[] args) {

		ApplicationContext appContext = new AnnotationConfigApplicationContext(AppConfig.class);

		ProductService service = appContext.getBean(ProductService.class);

		List<Product> products = service.findAll();

		for (Product product : products) {
			System.out.println(product);
		}

	}

}

Run the Program

Once you run the Application class, you will get a below output of all the products.

Product [productId=M1, productName=Mobile, price=400.0]
Product [productId=B1, productName=Book, price=10.0]

Autowire Beans using Constructor Injection

To autowire the beans at constructor level, add a constructor that initialize both BookDAO and MobileDAO reference variables using the arguments passed to the constructor. We can remove the field level @Autowired annotations.

package com.techstackjournal.service;

import java.util.List;

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

import com.techstackjournal.dao.BookDAO;
import com.techstackjournal.dao.MobileDAO;
import com.techstackjournal.model.Product;

@Service
public class ProductServiceImpl implements ProductService {

	private MobileDAO mobileDao;

	private BookDAO bookDao;

	@Autowired
	public ProductServiceImpl(MobileDAO mobileDao, BookDAO bookDao) {
		super();
		this.mobileDao = mobileDao;
		this.bookDao = bookDao;
	}

	public List<Product> findAll() {
		List<Product> productList = mobileDao.findAll();
		productList.addAll(bookDao.findAll());
		return productList;
	}

}

Autowire Beans using Setter Method Injection

To autowire the beans using setter methods, add setter methods for both BookDAO and MobileDAO annotating them with @Autowired annotation.

package com.techstackjournal.service;

import java.util.List;

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

import com.techstackjournal.dao.BookDAO;
import com.techstackjournal.dao.MobileDAO;
import com.techstackjournal.model.Product;

@Service
public class ProductServiceImpl implements ProductService {

	private MobileDAO mobileDao;

	private BookDAO bookDao;

	@Autowired
	public void setMobileDao(MobileDAO mobileDao) {
		this.mobileDao = mobileDao;
	}

	@Autowired
	public void setBookDao(BookDAO bookDao) {
		this.bookDao = bookDao;
	}

	public List<Product> findAll() {
		List<Product> productList = mobileDao.findAll();
		productList.addAll(bookDao.findAll());
		return productList;
	}

}

Mixing Autowire with Manual Injection

Say we only want to autowire MobileDAO type instance and provide the BookDAO type instance explicitly and programmatically into ProductServiceImpl.

To achieve this, we need to make some changes to our classes.

See also  Java based Spring Configuration

First, remove the @Service stereotype annotation for ProductServiceImpl as we’ve to explicitly create this bean by mixing the autowire for MobileDAO and manual wiring for BookDAO. Remove @Autowired for setBookDao method.

Remove @Repository stereotype annotation for BookDAOImpl as we want this bean to be manually created.

Explicitly create BookDAOImpl and ProductServiceImpl beans in AppConfig class and wire them together. Instance of MobileDAOImpl will be auto wired into ProductServiceImpl instance, so we don’t have to inject it explicitly.

package com.techstackjournal.app;

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

import com.techstackjournal.dao.BookDAO;
import com.techstackjournal.dao.BookDAOImpl;
import com.techstackjournal.service.ProductService;
import com.techstackjournal.service.ProductServiceImpl;

@ComponentScan({ "com.techstackjournal" })
@Configuration
public class AppConfig {

	@Bean
	public BookDAO getBookDAOImpl() {
		return new BookDAOImpl();
	}
	
	@Bean
	public ProductService getProductService() {
		ProductServiceImpl serviceImpl = new ProductServiceImpl();
		serviceImpl.setBookDao(getBookDAOImpl());
		return serviceImpl;
	}

}