Spring Framework

Dependency Injection Basics

What is dependency?

public class UserDao {    public User findById(Integer id) {
// execute a sql query to find the user
}
}
import javax.sql.DataSource;public class UserDao {    public User findById(Integer id) throws SQLException {
try (Connection connection = dataSource.getConnection()) { // (1)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where id = ?");
// use the connection etc.
}
}
}
  1. The question is now, where does your UserDao get its dataSource dependency from? The DAO obviously depends on a valid DataSource to fire those SQL queries.

How to instantiate dependencies with new()

import com.mysql.cj.jdbc.MysqlDataSource;public class UserDao {    public User findById(Integer id) {
MysqlDataSource dataSource = new MysqlDataSource(); // (1)
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
try (Connection connection = dataSource.getConnection()) { // (2)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where id = ?");
// execute the statement..convert the raw jdbc resultset to a user
return user;
}
}
}
  1. We want to connect to a MySQL database; hence we are using a MysqlDataSource and hardcoding URL/username/password here for easier reading.
  2. We use our newly created DataSource for the query.
import com.mysql.cj.jdbc.MysqlDataSource;public class UserDao {    public User findById(Integer id) {
try (Connection connection = newDataSource().getConnection()) { // (1)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where id = ?");
// TODO execute the select , handle exceptions, return the user
}
}
public User findByFirstName(String firstName) {
try (Connection connection = newDataSource().getConnection()) { // (2)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where first_name = ?");
// TODO execute the select , handle exceptions, return the user
}
}
public DataSource newDataSource() {
MysqlDataSource dataSource = new MysqlDataSource(); // (3)
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
return dataSource;
}
}
findById has been rewritten to use the new newDataSource() method.
  1. findByFirstName has been added and also uses the new data source() method.
  2. This is our newly extracted method, able to create new DataSources.
  1. What happens if we want to create a new ProductDAO class, which also executes SQL statements? Your ProductDAO would then also have a DataSource dependency, which now is only available in your UserDAO class. You would then have another similar method or extract a helper class that contains your DataSource.
  2. We are creating a completely new DataSource for every single SQL query. Consider that a DataSource opens up a real, socket connection from your Java program to your database. This takes time and is rather expensive. It would be much nicer if we opened just one DataSource and re-used it, instead of opening and closing tons of them. One way of doing this could be by saving the DataSource in a private field in our UserDao, so it can be reused between methods - but that does not help with the duplication between multiple DAOs.

How to 'manage' dependencies in a global Application class

import com.mysql.cj.jdbc.MysqlDataSource;

public enum Application {

INSTANCE;

private DataSource dataSource;

public DataSource dataSource() {
if (dataSource == null) {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
this.dataSource = dataSource;
}
return dataSource;
}
}
import com.yourpackage.Application;

public class UserDao {
public User findById(Integer id) {
try (Connection connection = Application.INSTANCE.dataSource().getConnection()) { // (1)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where id = ?");
// TODO execute the select etc.
}
}

public User findByFirstName(String firstName) {
try (Connection connection = Application.INSTANCE.dataSource().getConnection()) { // (2)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where first_name = ?");
// TODO execute the select etc.
}
}
}
  1. Your UserDAO does not have to construct its own DataSource dependency anymore, instead, it can ask the Application class to give it a fully-functioning one. Same for all your other DAOs.
  2. Your application class is a singleton (meaning there will only be one INSTANCE created), and that application singleton holds a reference to a DataSource singleton.
  1. The UserDAO actively has to know where to get its dependencies from, it has to call the application class → Application.INSTANCE.dataSource().
  2. If your program gets bigger, and you get more and more dependencies, you will have one monster Application.java class, which handles all your dependencies. At which point you’ll want to try and split things up into more classes/factories etc.

What is Inversion of Control?

import javax.sql.DataSource;public class UserDao {    private DataSource dataSource;    public UserDao(DataSource dataSource) { // (1)
this.dataSource = dataSource;
}
public User findById(Integer id) {
try (Connection connection = dataSource.getConnection()) { // (2)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where id = ?");
// TODO execute the select etc.
}
}
public User findByFirstName(String firstName) {
try (Connection connection = dataSource.getConnection()) { // (2)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where first_name = ?");
// TODO execute the select etc.
}
}
}
  1. Whenever a caller creates a new UserDao through its constructor, the caller also has to pass in a valid DataSource.
  2. The find methods will then simply use that DataSource.
public class MyApplication {    public static void main(String[] args) {
UserDao userDao = new UserDao(Application.INSTANCE.dataSource());
User user1 = userDao.findById(1);
User user2 = userDao.findById(2);
// etc ...
}
}

Dependency Injection Containers

What is an ApplicationContext?

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
public class MyApplication { public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); // (1)
UserDao userDao = ctx.getBean(UserDao.class); // (2)
User user1 = userDao.findById(1);
User user2 = userDao.findById(2);
DataSource dataSource = ctx.getBean(DataSource.class); // (3)
// etc ...
}
}
  1. Here we are constructing our Spring ApplicationContext. We’ll go into much more detail on how this works in the next paragraphs.
  2. The ApplicationContext can give us a fully configured UserDao, i.e. one with its DataSource dependency set.
  3. The ApplicationContext can also give us the DataSource directly, which is the same DataSource that it sets inside the UserDao.

How to create an ApplicationContext?

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); // (1)
// ...
}
}
What you really want to pass into the ApplicationContext constructor, is a reference to a configuration class, which should look like this:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyApplicationContextConfiguration { // (1)
@Bean
public DataSource dataSource() { // (2)
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
return dataSource;
}
@Bean
public UserDao userDao() { // (3)
return new UserDao(dataSource());
}
}
  1. You have a dedicated ApplicationContext configuration class, annotated with the @Configuration annotation, that looks a bit like the Application.java class from How to ‘manage’ dependencies in a global Application class.
  2. You have a method that returns a DataSource and is annotated with the Spring-specific @Bean annotation.
  3. You have another method, which returns a UserDao and constructs said UserDao by calling the DataSource bean method.
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApplication { public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyApplicationContextConfiguration.class);
UserDao userDao = ctx.getBean(UserDao.class);
// User user1 = userDao.findById(1);
// User user2 = userDao.findById(1);
DataSource dataSource = ctx.getBean(DataSource.class);
}
}
Now, let’s find out what exactly Spring and the AnnotationConfigApplicationContext do with that Configuration class you wrote.

Are there alternatives to AnnotationConfigApplicationContext?

What does the @Bean annotation do? What is a Spring Bean?

What are Spring bean scopes?

  • Should Spring create a singleton: All your DAOs share the same DataSource?
  • Should Spring create a prototype: All your DAOs get their own DataSource?
  • Or should your beans have even more complex scopes, like saying: A new DataSource per HttpRequest? Or per HttpSession? Or per WebSocket?
@Configuration
public class MyApplicationContextConfiguration {
@Bean
@Scope("singleton")
// @Scope("prototype") etc.
public DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
return dataSource;
}
}
  • Scope(“singleton”) → Your bean will be a singleton, there will only be one instance.
  • Scope(“prototype”) → Every time someone needs a reference to your bean, Spring will create a new one. (There’s a couple of caveats here, like injecting prototypes in singletons, though).
  • Scope(“session”) → There will be one bean created for every user HTTP session.

What is Spring’s Java Config?

@Configuration
public class MyApplicationContextConfiguration {
@Bean
public DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
return dataSource;
}
@Bean
public UserDao userDao() { // (1)
return new UserDao(dataSource());
}
}
  1. One question: Why do you have to explicitly call new UserDao() with a manual call to dataSource()? Cannot Spring figure all of this out itself?

What does @ComponentScan do?

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan // (1)
public class MyApplicationContextConfiguration {
@Bean
public DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
return dataSource;
}
// (2) // no more UserDao @Bean method!
}
  1. We added the @ComponentScan annotation.
  2. Note, that the UserDAO definition is now missing from the context configuration!

What do @Component & @Autowired do?

import javax.sql.DataSource;
import org.springframework.stereotype.Component;
@Component
public class UserDao {
private DataSource dataSource; public UserDao(DataSource dataSource) { // (1)
this.dataSource = dataSource;
}
}
  1. This tells Spring, similarly to that @Bean method you wrote before: Hey, if you find me annotated with @Component through your @ComponentScan, then I want to be a Spring bean, managed by you, the dependency injection container!
import javax.sql.DataSource;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@Component
public class UserDao {
private DataSource dataSource; public UserDao(@Autowired DataSource dataSource) {
this.dataSource = dataSource;
}
}
  • UserDAO is annotated with @Component → Spring will create it
  • UserDAO has an @Autowired constructor argument → Spring will automatically inject the DataSource that is configured via your @Bean method
  • Should there be no DataSources configured in any of your Spring configurations, you will receive a NoSuchBeanDefinition exception at runtime.

Constructor Injection & Autowired Revisited

@Component
public class UserDao {
private DataSource dataSource; public UserDao(DataSource dataSource) {
this.dataSource = dataSource;
}
}

What are Field and Setter Injection?

import javax.sql.DataSource;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@Component
public class UserDao {
@Autowired
private DataSource dataSource;
}
import javax.sql.DataSource;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@Component
public class UserDao {
private DataSource dataSource; @Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}These two injection styles (fields, setters) have the same outcome as constructor injection: You’ll get a working Spring Bean. (In fact, there’s also another one, called method injection which we won’t cover here.)

Constructor Injection vs. Field Injection

  1. I have worked with both styles, constructor injection, and field injection in various projects over the recent years. Based solely on personal experience, I do not truly favor one style over the other.
  2. Consistency is king: Do not use constructor injection for 80% of your beans, field injection for 10%, and method injection for the remaining 10%.
  3. Spring’s approach from the official documentation seems sensible: Use constructor injection for mandatory dependencies and setter/field injection for optional dependencies. Be warned: Be really consistent with that.

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

2 Must-Know Tricks of Unit Testing with Spring Boot

Upgradeable Contracts: Eternal Storage example using ZeppelinOS and Truffle

Simple WebSockets with Spring Boot

Screen-Shot-2019-04-16-at-2.14.00-PM

Speaker Interview: Michele Bertoli — Front End Engineer at Facebook.com

Design Patterns in Ruby: Memento

Command Line Cheat Sheet for Data Scientists

Photo by Andre Sebastian on Unsplash

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Kotasaisrikanth

Kotasaisrikanth

More from Medium

NUMBERING SYSTEM EDGE CASES OF JAVA

SOLID design principles — Craftsman approach to code that lasts

HUAWEI MOBILE SERVICE (HMS)

Performance /Load Testing Phases