Spring Framework

Kotasaisrikanth
13 min readDec 13, 2020

--

At its core, the Spring framework is really just a dependency injection container, with a couple of convenience layers (think: database access, proxies, aspect-oriented programming, RPC, a web MVC framework) added on top. It helps you build Java applications faster and more conveniently.

Dependency Injection Basics

What is dependency?

Imagine you are writing a Java class that lets you access a users table in your database. You would call these classes DAOs (data access objects) or Repositories. So, you are going to write a UserDAO class.

public class UserDao {    public User findById(Integer id) {
// execute a sql query to find the user
}
}

Your UserDAO has only one method which lets you find users in your database table by their respective IDs.

To execute the appropriate SQL query, your UserDAO needs a database connection. And in the Java world, you (usually) get that database connection from another class, called a DataSource. So, your code now would look something like this:

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()

The naive approach would be to simply create a new DataSource through a constructor, every time you need one. So, to connect to a MySQL database your UserDAO could look like this:

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.

This works, but let’s see what happens when we extend our UserDao class with another method, findByFirstName.

Unfortunately, that method also needs a DataSource to work with. We can add that new method to our UserDAO and apply some refactorings, by introducing a new Datasource method.

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.

This approach works, but has two drawbacks:

  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

To accommodate these issues, you could think about writing a global Application class, that looks something like this:

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;
}
}

Your UserDAO could now look like this:

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

It is an improvement in two ways:

  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.

There are however still several drawbacks to this solution:

  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?

Let’s go one step further.

Wouldn’t it be nice if you and the UserDAO didn’t have to worry about finding dependencies at all? Instead of actively calling Application.INSTANCE.dataSource(), your UserDAO could shout (somehow) that it needs one, but has no control anymore when/how/where it gets it from?

This is what is called inversion of control.

Let’s have a look at what our UserDAO could look like, with a brand-new constructor.

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.

From the UserDao perspective, this reads much nicer. It doesn’t know about the application class anymore, or how to construct DataSources itself. It only announces to the world “if you want to construct (i.e. use) me, you need to give me a Datasource”.

But imagine you now want to run your application. Whereas you could call “new UserService()” previously, you’ll now have to make sure to call new UserDao(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

Hence, the issue is that you, as a programmer are still actively constructing UserDAOs through their constructor and thus setting the DataSource dependency manually.

Wouldn’t it be nice if someone knew that your UserDAO has a DataSource constructor dependency and knew how to construct one? And then magically construct both objects for you: A working DataSource and a working UserDao?

That someone is a dependency injection container and is exactly what Spring framework is all about.

What is an ApplicationContext?

That someone, who has control over all your classes and can manage them appropriately (read: create them with the necessary dependencies), is called ApplicationContext in the Spring universe.

What we want to achieve is the following code (I described the UserDao and DataSource in the previous section, go skim it if you came right here and skipped it):

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.

This is pretty cool, isn’t it? You as the caller don’t have to worry about constructing classes anymore, you can simply ask the ApplicationContext to give you working ones!

But how does that work?

How to create an ApplicationContext?

In the code above, we put a variable called “someConfigClass” in the AnnotationConfigApplicationContext constructor. Here’s a quick reminder:

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.

This configuration class is already enough to run your very first Spring application.

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?

There are many ways to construct a Spring ApplicationContext, for example through XML files, annotated Java configuration classes, or even programmatically. To the outside world, this is represented through the single ApplicationContext interface.

Look at the MyApplicationContextConfiguration class from above. It is a Java class that contains Spring-specific annotations. That is why you would need to create an Annotation ConfigApplicationContext.

If instead, you wanted to create your ApplicationContext from XML files, you would create a ClassPathXmlApplicationContext.There are also many others, but in a modern Spring application, you will usually start with an annotation-based application context.

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

You’ll have to think of the methods inside your ApplicationContext configuration class as factory methods. For now, there is one method that knows how to construct UserDao instances and one method that constructs DataSource instances. These instances that those factory methods create are called beans. It is a fancy word for saying: I (the Spring container) created them and they are under my control.

But this leads to the question: How many instances of a specific bean should Spring create?

What are Spring bean scopes?

How many instances of our DAOs should Spring create? To answer that question, you need to learn about 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?

You can read up on a full list of available bean scopes here, but for now, it is sufficient to know that you can influence the scope with yet another annotation.

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

@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;
}
}

The scope annotation controls how many instances Spring will create. And as mentioned above, that’s rather simple:

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

The gist: Most Spring applications almost entirely consist of singleton beans, with the occasional other bean scopes (prototype, request, session, WebSocket, etc.) sprinkled in.

Now that you know about ApplicationContexts, Beans & Scopes, let’s have another look at dependencies or the many ways our UserDAO could obtain a DataSource.

What is Spring’s Java Config?

So far, you explicitly configured your beans in your ApplicationContext configuration, with the help of @Bean annotated Java methods.

This is what you would call Spring’s Java Config, as opposed to specifying everything in XML, which was historically the way to go with Spring. Just a quick recap of what this looks like:

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

@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?

This is where another annotation called @ComponentScan comes in.

What does @ComponentScan do?

The first change you’ll need to apply to your context configuration is to annotate it with the additional @ComponentScan annotation.

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 this @ComponentScan annotation does, is tell Spring: Have a look at all Java classes in the same package as the context configuration if they look like a Spring Bean!

This means if your MyApplicationContextConfiguration lives in package com. Marco Behler, Spring will scan every package, including subpackages, that starts with com. Marco Behler for potential Spring beans.

How does Spring know if something is a Spring bean? Easy: Your classes need to be annotated with a marker annotation, called @Component.

What do @Component & @Autowired do?

Let’s add the @Component annotation to your UserDAO.

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!

(When you look at the source code of annotations like @Controller, @Service, or @Repository later on, you’ll find that they all consist of multiple, further annotations, always including @Component!).

There’s only one little piece of information missing. How does Spring know that it should take the DataSource that you specified as a @Bean method and then create new UserDAOs with that specific DataSource?

Easy, with another marker annotation: @Autowired. Hence, your final code will look like this.

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;
}
}

Now, Spring has all the information it needs to create UserDAO beans:

  • 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

I have been lying to you a tiny bit in the previous section. In earlier Spring versions (pre 4.2, see history), you needed to specify @Autowired for constructor injection to work.

With newer Spring versions, Spring is actually smart enough to inject these dependencies without an explicit @Autowired annotation in the constructor. So this would also work.

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

Why did I mention @Autowired then? Because it does not hurt, i.e. makes things more explicit, and because you can use @Autowired in many other different places, apart from constructors.

Let’s have a look at different ways of dependency injection — constructor injection just being one many.

What are Field and Setter Injection?

Simply put, Spring does not have to go through a constructor to inject dependencies.

It can also directly inject fields.

import javax.sql.DataSource;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@Component
public class UserDao {
@Autowired
private DataSource dataSource;
}

Alternatively, Spring can also inject setters.

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

But obviously, they differ from one another, which means there has been a great many debates about which injection style is best and which one you should use in your project.

Constructor Injection vs. Field Injection

There have been a great many debates online, whether constructor injection or field injection is better, with a number of strong voices even claiming that field injection is harmful.

To not add further noise to these arguments, the gist of this article is:

  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.

Most importantly, keep in mind: The overall success of your software project will not depend on the choice of your favorite dependency injection method (pun intended).

--

--