Spring Fundamentals, Visualized

This is part of a series to understand Spring and Spring Boot. Let’s go through some of the fundamentals before jumping into Spring Boot (next article). These descriptions will have more code than I typically like to use but it will help with the visualizations.

We’ll take a look at Beans, Inversion of Control, and Dependency Injection.

Beans

Simple bean. Just a container for your object

A bean is a container for a generic object you may create. Let’s say you have an AuthService class and it contains some business logic (to create public/private keys or bearer tokens or whatever). A bean just wraps around your AuthService class!

An auth service wrapped in a bean

Inversion of Control

Spring creating a bean.

Let’s look at the code. Without Spring, we’d probably initialize the Auth Service like this:

public class Main{     public static void main(String []args){
AuthService authService = new AuthService();
}

}
Just initializing the Auth Service

With Spring, initialization would look like this(and this is one way of many ways). The @Bean annotation tells spring to create a bean with our service:

@SpringBootApplication // Don't worry about this
public class Main{
public static void main(String []args){}

@Bean
public AuthService createAuthService() {
return new AuthService()
}

}

We can visualize the section above like this:

Creating a bean surrounding the Auth Service

With Spring, we don’t directly initialize the AuthService. Spring initializes the AuthService class for us. And letting Spring manage the AuthService object is an example of Inversion of Control! We are giving control to the framework, Spring.

Dependency Injection

Spring managing the Auth Service

Now, imagine we have a new class, UserService. This service may handle creating, deleting, and modifying users. UserService needs to access a method from the AuthService to create a token. Let’s look at these two services.

AuthService:

class AuthService {    public String createToken() {
// some logic to create token
}
}

UserService:

class UserService {   private AuthService authService;   UserService(AuthService authService) {
this.authService = authService;
}
public void createUser() {
// some other logic
authService.createToken()
// ...
}
}

Without Spring, we could pass the the AuthService as a parameter to the UserService class. Then UserServivce will have a reference to the AuthService and can easily call createToken(). We could do all of this in the code below:

public class Main{     public static void main(String []args){
AuthService authService = new AuthService();
UserService userService = new UserService(authService);
}

}

Okay. What just happened? We initialized and passed an AuthService object to the UserService constructor. Now, UserService can use createToken().

And if we used Spring? Well, let’s go back to initialization. Similar to what we had before. Code:

@SpringBootApplication // Worry about this later
public class Main{
public static void main(String []args){}

@Bean
public AuthService createAuthService() {
return new AuthService()
}

@Bean
public UserService createUserService() {
return new UserService();
}
}

And a way to visualize:

Creating two user and auth service beans!

We’re also going to make a slight modification to the UserService. We’re going to remove our constructor and add in an Autowired annotation:

class UserService {   @Autowired
private AuthService authService;
public void createUser() {
// some other logic
authService.createToken()
// ...
}
}

Using Autowired will tell Spring to inject an instance of the AuthService into the UserService:

Spring injecting Auth Service into User Service

And using @Autowired is dependency injection! We’re letting Spring handle passing the initialized AuthService object to the UserService object. Spring is handling creating and setting up the dependencies as another example of Inversion of Control.

Why

Advantages:

  • We focus on the core business logic and not on the dependencies.
  • It becomes easier to write modular code and follow best practices.
  • Spring makes it easy for us to mock out dependencies when testing!

Disadvantages:

  • The learning curve (which hopefully is lowered by an article like this).
  • Can make code difficult to follow.

Summary

This is part of a series to visualize Spring Fundamentals and Spring Boot: (part 2 coming soon).

Software engineer by day, software engineer by night.