Logo

dev-resources.site

for different kinds of informations.

Understanding the Spring Framework: A Developer’s Journey to Clean Code 🚀

Published at
1/6/2025
Categories
webdev
java
spring
Author
kaweeshamr
Categories
3 categories in total
webdev
open
java
open
spring
open
Author
10 person written this
kaweeshamr
open
Understanding the Spring Framework: A Developer’s Journey to Clean Code 🚀

Introduction: The Early Java Era

In the early era of programming, writing Java code was a bit frustrating. Developers had to set up long configurations, deal with verbose XML files, and manually manage dependencies that were rapidly updated. This era made it harder for developers to thrive and write efficient code.

In 2003, Spring Framework revolutionized the Java ecosystem by introducing Dependency Injection (DI) and Inversion of Control (IoC). These features significantly reduced developers' headaches by eliminating tightly coupled code and making it easier to write clean, modular code. 🙌


What is Inversion of Control (IoC)? 🤔

IoC Diagram

IoC is a design principle where the control of object creation and dependency management is transferred to a container (like the Spring IoC container). Instead of objects managing their own dependencies, the container does this work for you! 🎉

Normal (Without IoC) Behavior

Without IoC, developers are responsible for creating and managing their object dependencies.

For example, let’s consider a Car class that depends on an Engine class. In this scenario, the Car class manages the Engine object by itself:

public class Car {
    private Engine engine = new Engine();  // Car directly creates an Engine

    public void start() {
        engine.run();
    }
}
Enter fullscreen mode Exit fullscreen mode

In this case:

  • The Car class directly creates the Engine object.
  • The Car class is responsible for both creating and managing the Engine object, which creates a tight coupling between the two.
  • If we want to change the Engine class (for example, to use a HybridEngine), we’d need to modify the Car class. This violates the Open/Closed Principle: classes should be open for extension but closed for modification. 🚧

With IoC in Spring (Dependency Injection) 🛠️

In IoC and Dependency Injection (DI), Spring takes care of creating and injecting dependencies into your classes.

Simply put, the Spring IoC container creates the Engine object and injects it into the Car object when required. This way, you don’t have to manually manage object creation or dependencies. Spring handles it for you. 🧑‍💻

public class Car {
    private Engine engine;  // Engine is injected by Spring, not created by Car

    // Constructor-based injection
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.run();
    }
}
Enter fullscreen mode Exit fullscreen mode

How Does Spring Know Which Objects to Inject? 🧐

Spring uses Spring Beans to manage the lifecycle and dependencies of objects. In Spring, a bean is an object that the Spring IoC container manages. The container knows which beans to create and inject based on your configuration. 🌱

Spring Beans and Bean Lifecycle 🔄

In Spring, a bean is any object that is managed by the Spring IoC container. The container is responsible for:

  1. Instantiation: Creating the bean.
  2. Dependency Injection: Injecting the required dependencies.
  3. Initialization: Calling any initialization methods (e.g., @PostConstruct).
  4. Destruction: Calling cleanup methods (e.g., @PreDestroy).

How to Define a Spring Bean? 🏷️

You can define beans using annotations:

  • @Component: Marks a class as a Spring-managed bean. Spring automatically detects and instantiates this class during component scanning.
  • @Autowired: Tells Spring to inject the required dependency (either via constructor, field, or setter).
  • @Configuration: Used to define bean definitions using Java configuration (using @Bean).

Example with annotations:

@Component
public class Car {
    private Engine engine;

    @Autowired
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.run();
    }
}

@Component
public class Engine {
    public void run() {
        System.out.println("Engine is running...");
    }
}
Enter fullscreen mode Exit fullscreen mode

Advantages of IoC (and DI) 🏆

By using IoC and DI, Spring offers several advantages:

  1. Loose Coupling: The Car class is no longer tightly coupled with the Engine. It only depends on the interface or type, not the specific implementation. This makes it easy to swap the Engine type, such as using an ElectricEngine instead of a GasolineEngine. 🔄

  2. Easier Testing: Since dependencies are injected, you can easily substitute mock objects or different implementations when writing unit tests for the Car class. 🧪

  3. Flexibility: You can change the dependencies externally (e.g., through Spring configuration) without modifying the core business logic of the classes. 🔧


Spring Configuration Methods

There are three primary ways to configure Spring beans and the IoC container:

1. XML Configuration (Traditional) 📜

In the traditional approach, beans are defined in an XML configuration file. This method is becoming less common but is still used in legacy systems.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="car" class="com.example.Car">
        <constructor-arg ref="engine"/>
    </bean>

    <bean id="engine" class="com.example.Engine"/>
</beans>
Enter fullscreen mode Exit fullscreen mode

2. Annotation-based Configuration (Modern) 🏷️

The modern approach uses annotations like @Component, @Autowired, and @Configuration to define and inject beans.

@Component
public class Car {
    private Engine engine;

    @Autowired
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.run();
    }
}
Enter fullscreen mode Exit fullscreen mode

To enable annotation-based configuration, use @ComponentScan in a configuration class:

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    // Spring scans the specified package for @Component annotated classes
}
Enter fullscreen mode Exit fullscreen mode

3. Java-based Configuration (Best for Type Safety) 🖥️

Java-based configuration uses @Configuration and @Bean annotations, which is more type-safe and easier to maintain.

@Configuration
public class AppConfig {

    @Bean
    public Car car() {
        return new Car(engine());
    }

    @Bean
    public Engine engine() {
        return new Engine();
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion 🏁

In this blog, we’ve explored:

  • IoC (Inversion of Control): The core design principle where Spring takes control of object creation and dependency management.
  • DI (Dependency Injection): A technique that helps Spring inject dependencies into your beans, allowing for clean and flexible code.
  • Spring Beans: Objects that are managed by Spring, and how their lifecycle is handled by the IoC container.
  • Spring Configuration: Different ways to configure Spring beans, from XML to annotations to Java-based configurations.

Spring's powerful IoC and DI capabilities help you write cleaner, more modular, and easily testable code. 🌟


spring Article's
30 articles in total
Favicon
Launched a Web version of my Project using Java Spring Framework, Spring Boot Web
Favicon
Developing a project using Java Spring Framework, JSON, JPA and PostgreSQL
Favicon
Волшебные скоупы: Как Spring организует работу бинов
Favicon
Quando usar ResponseEntity?
Favicon
What is load balancing and how to do it on client side
Favicon
Capturing and Testing Logs in Java with SLF4J and Logback: A Simple Guide
Favicon
Can you find the Output of this Java Code
Favicon
Ways to Speed Up Spring Boot Application Startup Time
Favicon
Apache wicket with spring boot example application: notice board
Favicon
Handling the "Forwarded" Header in Spring Boot Applications
Favicon
About UriComponentsBuilder and UriComponents
Favicon
Spring Boot: About @SpringBootApplication
Favicon
Spring Security: CSRF protection
Favicon
ISBN Stacks — A look at a possible Spring Application implementation without annotations
Favicon
spring profiles dev production
Favicon
Learn Spring Data JPA, Part - 1
Favicon
Methods for Efficient Large File Processing in Spring Boot
Favicon
Hexagonal Architecture — A Favorite Lyrics Spring Boot — Java Example
Favicon
Validation in Spring REST Framework (SRF)
Favicon
spring
Favicon
Spring Security: Redirect to login page if access pages which is for authorized user only
Favicon
Lambda vs. Named Functions: Choosing the Right Tool for the Job
Favicon
Techniques for Mastering Spring Interceptors: Detailed Guide with Examples
Favicon
Techniques for Using @NoRepositoryBean in Spring Data JPA: A Comprehensive Guide
Favicon
Understanding the Spring Framework: A Developer’s Journey to Clean Code 🚀
Favicon
@PreConstruct and @PostConstruct annotation Spring Boot Example
Favicon
Java’s Functional Programming: the OOP influence
Favicon
🧪 Тестирование с TestRestTemplate и MockMvc: миссия "Котики против багов" 🐞
Favicon
spring boot
Favicon
Reasons to Use WireMock for Testing REST APIs in Spring Boot Applications

Featured ones: