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 infoBookDAOandMobileDAO– an interface that we will use in a dependent service classBookDAOImplandMobileDAOImpl– implementation classes forBookDAOandMobileDAOImplreturns list of mocked product objectsProductService– an interface that we will use in the application to hold a service instanceProductServiceImpl– an implementation ofProductServiceand the dependent ofMobileDAOandBookDAOinstancesApplication– It will get theProductServiceinstance to fetch productsAppConfig– 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.
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.
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.
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;
}
}
