Core Concepts

Loose Coupling

Loose Coupling is a design principle that aims to reduce the dependencies between components within a system.


Inversion of Control (IoC)

Inversion of Control is a design principle where the control of object creation and lifecycle management is transferred from the application code to an external container or framework.


Dependency Injection (DI)

Dependency Injection is a design pattern commonly used in object-oriented programming, where the dependecies of a class are provided externally rather than being created within the class itself.


Beans

Beans are objects that are managed by the Spring framework.


Container

A Spring Container is responsible for managing the life cycle, configuration, and dependencies injection of the beans in a Spring Application.


Life Cycle of Beans

  1. Beans are objects that are managed by the Spring framework.
  2. Bean Definition includes configuration meta data required by the Spring container to create and manage the bean.
  3. Bean Configurations can be provided in various way, including XML configuration files, annotations, and Java-based configuration.
  4. Bean Instantiation $\rightarrow$ Population of Properties $\rightarrow$ Initialization $\rightarrow$ Ready for Use $\rightarrow$ Destruction

The Difference between instantiation and initiation?

Instantiation occurs when a new keyword is used to create an object. It focus on constructing the object’s structure and allocating memory. In contrast, initialization focus on assigning values to the object’s fields and setting up its initial state after instantiation.


Constructor Injection & Setter Injection & Field Injection

Constructor Injection

Constructor Injection in Spring is a type of dependency injection where the Spring framework provides the required dependency to a class by invoking its constructor. Since dependencies are supplied via the constructor, they cannot be changed after object creation – promoting immutability. However, this approach may cause circular dependency issues if two or more beans depend on each other in a circular manner.

SHOW CODE
@Component
public class ServiceA {
    private final RepositoryA repositoryA;

    // Constructor injection
    @Autowired
    public ServiceA(RepositoryA repositoryA) {
        this.repositoryA = repositoryA;
    }
}

Setter Injection

Setter Injection in Spring is a form of dependency injection where the Spring framework injects dependencies into a bean by calling the bean’s setter method. This approach is flexible and commonly used to inject optional dependencies or when the dependency might change after the object is initialized.

SHOW CODE
@Component
public class ServiceB {
    private RepositoryB repositoryB;

    // Setter injection
    @Autowired
    public void setRepositoryB(RepositoryB repositoryB) {
        this.repositoryB = repositoryB;
    }
}

Field Injection

Field Injection in Spring is a form of dependency injection where the Spring framework directly injects dependencies into a class’s fields. Like constructor injection, the dependencies remain immutable after being injected. Under the hood. Spring uses reflection to assign values to private fields.

SHOW CODE
@Component
public class ServiceC {
    @Autowired
    private RepositoryC repositoryC;

    public void performAction() {
        repositoryC.doSomething();
    }
}

@Component & @ComponentScan

The @Component annotation in Spring is used to mark a class as spring-managed bean, allowing Spring to detect and register it as a bean in the application context. The @ComponentScan annotation is used to specify base packages that Spring should scan for @Component-annotated during configuration.

SHOW CODE
package com.example.myapp
@Component
public class MyService {
    public void performTask() {
        System.out.println("Task performed!");
    }
}

@Configuration
@ComponentScan(basePackages = "com.example.myapp")
public class AppConfig {
}


@Value & @Autowired & @Qualifier

@Value

The @Value annotation is used to inject values into fields, mathod parameters, and constructor arguments.

SHOW CODE
// Injects literal values
@Value("Hello, World!")
private String message;

// Injects from a properties file
app.name=MySpringApp
@Value("${app.name}")
private String appName;

// Injects default values
@Value("${app.version:1.0.0}")
private String appVersion;

// Injects expression
@Value("#{T(java.lang.Math).random() * 100}")
private double randomValue;


@Autowired

The @Autowired annotation is used for automatic dependency injection. It allows Spring resolve and inject the required bean into a class automatically by type.

SHOW CODE
// Field Injection
@Component
public class ServiceA {
    @Autowired
    private RepositoryA repositoryA;

    public void performTask() {
        repositoryA.doSomething();
    }
}

// Setter Injection
@Component
public class ServiceB {
    private RepositoryB repositoryB;

    @Autowired
    public void setRepositoryB(RepositoryB repositoryB) {
        this.repositoryB = repositoryB;
    }
}

// Constructor Injection
@Component
public class ServiceB {
    private RepositoryB repositoryB;

    @Autowired
    public void setRepositoryB(RepositoryB repositoryB) {
        this.repositoryB = repositoryB;
    }
}

@Qualifier

The @Qualifier annotation in Spring is used for resolve ambiguities when multiple beans of the same type are available in the application context.

SHOW CODE
@Component("repositoryImpl1")
public class RepositoryImpl1 implements Repository {}

@Component("repositoryImpl2")
public class RepositoryImpl2 implements Repository {}

@Component
public class Service {
    @Autowired
    @Qualifier("repositoryImpl2")
    private Repository repository;
}

Components of Spring Boot

Spring Boot Starter: Simplifies dependency management by building commonly used dependencies, reducing the need for manual configuration.

Auto Configuration: Automatically configures Spring Boot application beans and settings based on the libraries on the classpath, eliminating the need for extensive configuration in application.properties or XML files.

Spring Boot Atuator: Provides production-ready features like monitoring, metrics, and health checks to ensure better application insights and management.

Embedded Server: Includes servers like Tomcat and Jetty, allowing applications to run as standalone programs, simplifying development and deployment.

Spring Boot DevTools: A collection of tools that enhance the development experience by enabling features like hot reload and live reload for faster feedback.


Spring Boot Auto Configuration

Spring Boot looks for classes annotated with @Configuration and examines the dependencies in the classpath. The @Configuration annotation triggers the logic that looks for @Bean declarations within the class and registers those beans to be managed by the Spring container.

In a Spring Boot application, the main method typically calls the SpringApplication.run() method. This method takes the main application class (e.g., XXXApplication.class) as a parameter. The XXXAplication class is annotated with @SpringBootApplication which includes three key annotations: @SpringBootConfiguration, @EnableAutoConfiguration, and @ComponentScan.

@SpringBootConfiguration is an alias for @Configurition. It indicates that the class contains Spring bean definitions, and Spring Boot will scan it for @Bean declarations.

@EnableAutoConfiguration annotation tells Spring Boot to enable its auto-configuration mechanism. It looks for the file AutoConfiguration.imports inside the spring-boot-autoconfigure.jar dependency, which lists auto-configuration classes. Spring Boot automatically imports and executes every class that has the AutoConfiguration name at the end of the class name.

# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
...

@ComponentScan annotation tells Spring Boot to scan for Spring components (such as @Component, @Service, @Repository, etc.) in the specified packages or the entire classpath. By default, it scans from the package of the class that contains the annotation. To specify a different base package, the basePackages parameter can be set, like this:

@ComponentScan(basePackages = {"com.mycompany"})

Common Annotations

@Component vs. @Bean

The @Component annotation is applied to a class to mark it as a bean managed by the Spring container. There are several specialized variants of this annotation: @Controller (for web controllers), @Service (for service layer components), and @Repository (for DAO layer components). In contrast, the @Bean annotation is used to mark a method within a class annotated with @Configuration, indicating that the method should return an object to be managed by the Spring container. This apporpach provides more control over bean creation and configuration, allowing for more explicit customization of the bean.

SHOW CODE
// Spring will detect UserService during classpath 
// scanning and manage it as a bean.
@Component
public class UserService {
    public String getUser() {
        return "User";
    }
}

@Configuration
public class AppConfig {

    @Bean
    public UserService userService() {
        // Explicitly creating and returning the bean
        return new UserService(); 
    }
}

@ResposeBody

The @ResponseBody annotation in Spring is used to indicate that a method’s return value should be written directly to the HTTP response body, rather than being treated as a view name, which is the default behavior in traditional Spring MVC controllers. The return value is automatically serialized into a format like JSON or XML, typically using Jackson, depending on the request’s Accept header.

SHOW CODE
@Controller
public class MyRestController {

    @RequestMapping("/user")
    @ResponseBody
    public User getUser() {
        return new User("John", "Doe");
    }
}

public class User {
    private String firstName;
    private String lastName;

    // Constructor, getters, and setters
}

In the above code, the User object will be automatically serialized into JSON format by Jackson when returned from the getUser() method. The @ResponseBody annotation ensures that the method’s return value is written directly to the HTTP response body, and Jackson handles the conversion to JSON.


@Controller vs. @RestController

The @Controller annotation is used to declare a class as a controller in Spring MVC. By default, methods within a @Controller-annotated class return a view name (e.g., home.jsp). If a method needs to return a value directly (such as JSON or XML), it should be annotated with @ResponseBody. In contrast, the @RestController annotation is a specialized version of @Controller used to define a controller for RESTful web services. It combines @Controller and @ResponseBody, meaning that the methods in a RestController-annotated class automatically return values written directly to the HTTP response body. This response is typically serialized into JSON or XML format by Jackson.

SHOW CODE
@Controller
public class WebController {
    @RequestMapping("/home")
    public String home() {
        return "home"; // returns home.jsp
    }
}

@Controller
public class MyController {

    @RequestMapping("/greeting")
    @ResponseBody
    public String greeting() {
        return "Hello, World!"; // The return value is written directly to the response body
    }
}


@RestController
public class UserController {

    @RequestMapping("/api/user")
    public User getUser() {
        return new User("Alice", "Smith");
    }
}

public class User {
    private String firstName;
    private String lastName;

    // Constructor, getters, and setters
}

@RequestBody, @RequestParam, @PathVariable

The @RequestBody annotation is used to bind the body of an HTTP request to a method parameter. It is typically used with HTTP methods like POST, PUT, or PATCH to send data in formats such as JSON or XML. Spring automatically deserializes the request body into an obejct using a message converter, liek Jakson.

SHOW CODE
@RestController
public class UserController {

    @PostMapping("/user")
    public ResponseEntity<String> createUser(@RequestBody User user) {
        // User is automatically deserialized from the JSON request body
        return ResponseEntity.ok("User created: " + user.getName());
    }
}


The @RequestParam annotation binds a query parameter or form data to a method parameter. It is commonly used in GET or POST requests to retrive single query parameters. The annotation has optional attributes such as required (to specify if the parameter is mandatory) and defaultValue (to provide a default value when the parameter is absent).

SHOW CODE
@RestController
public class SearchController {

    @GetMapping("/search")
    public ResponseEntity<String> search(@RequestParam String query, @RequestParam(defaultValue = "10") int limit) {
        // query: the search term from the query string
        // limit: the number of results (defaults to 10 if not provided)
        return ResponseEntity.ok("Searching for: " + query + ", Limit: " + limit);
    }
}

The @PathVariable annotation binds a URL path variable to a method parameter, commonly used in RESTful web service to capture dynamic segments in the URL. For example, int the URL /user/{id}, the {id} part is dynamic, and the value of {id} will be extracted and passed to the method parameter.

SHOW CODE
@RestController
public class UserController {

    // Regular expression to match both with and without the path variable
    @GetMapping("/user/{id:[a-zA-Z0-9]*}")
    public ResponseEntity<String> getUser(@PathVariable(required = false) String id) {
        if (id == null) {
            return ResponseEntity.ok("Default User");
        }
        return ResponseEntity.ok("User ID: " + id);
    }
}

Transformation between Object and Spring

In Spring Boot, the transformation between objects and JSON is handled by the Jackson library which allows for easy serialization (converting an object to JSON) and deserialization (converting JSON to an object).

SHOW CODE
// When a controller returns an object, Spring Boot automatically 
// converts it to JSON
@RestController
public class UserController {
    @GetMapping("/user")
    public User getUser() {
        return new User(1, "John Doe", "john.doe@example.com");
    }
}

// When accessed via /user, the response is:
{
    "id": 1,
    "name": "John Doe",
    "email": "john.doe@example.com"
}

// Spring Boot automatically converts the incoming JSON data into 
// an object when the object is a parameter in the controller method.
@RestController
public class UserController {
    @PostMapping("/user")
    public String createUser(@RequestBody User user) {
        return "User " + user.getName() + " created!";
    }
}

// Input JOSN:
{
    "id": 2,
    "name": "Jane Doe",
    "email": "jane.doe@example.com"
}