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 infoBookDAO
andMobileDAO
– an interface that we will use in a dependent service classBookDAOImpl
andMobileDAOImpl
– implementation classes forBookDAO
andMobileDAOImpl
returns list of mocked product objectsProductService
– an interface that we will use in the application to hold a service instanceProductServiceImpl
– an implementation ofProductService
and the dependent ofMobileDAO
andBookDAO
instancesApplication
– It will get theProductService
instance 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;
}
}