Simplifying Dependency Management with Spring IoC

Author:

Introduction

Imagine writing an application where you have to create and manage connections with your database system, use Kafka, set up a web server, configure a cache system, and handle JSON serialization and deserialization, among many other components. The amount of work required just to set up these dependencies before starting to develop features that bring real value can be overwhelming. Moreover, maintaining and setting these dependencies across all components of your code can be cumbersome.

In the past, many developers dealt with this by employing various design patterns such as Factories, Builders, Proxies, Service Locators, reflection, and many other methods. While these helped, components often became tightly coupled and overly knowledgeable about their dependencies.

Traditional Factory Method

Here’s an example of a factory method to deal with dependency creation without Spring:

public class PaymentProcessorFactory {

    public static PaymentProcessor createService(String type) {
        if (type.equalsIgnoreCase("paypal")) {
            return new PayPalPaymentProcessor();
        } else if (type.equalsIgnoreCase("stripe")) {
            return new StripePaymentProcessor();
        } else {
            throw new IllegalArgumentException("Invalid payment processor type: " + type);
        }
    }
}

// Usage
PaymentProcessor paypalProcessor = PaymentProcessorFactory.createService("paypal");
PaymentProcessor stripeProcessor = PaymentProcessorFactory.createService("stripe");


While this method works, it has limitations. Adding more methods requires changing the code, which makes it tightly coupled and hard to maintain.

Introducing Spring IoC

To address these issues, Spring introduced the Spring IoC (Inversion of Control) container, which is part of the Spring application context.

@Configuration
public class PaymentProcessorConfig {

    @Bean
    @ConditionalOnProperty(prefix = "payment", name = "processor", havingValue = "stripe")
    public PaymentProcessor stripePaymentProcessor() {
        return new StripePaymentProcessor();
    }

    @Bean
    @ConditionalOnProperty(prefix = "payment", name = "processor", havingValue = "paypal")
    public PaymentProcessor paypalPaymentProcessor() {
        return new PayPalPaymentProcessor();
    }
}


Understanding IoC

Each component is a bean. A bean can be an infrastructure bean (like repositories for interacting with databases, Kafka templates for sending messages) or components created by developers to support business rules.

IoC means Inversion of Control. Instead of a component calling a factory for a Kafka template to send messages, Spring handles this and delivers a fully functional bean to the component. The component no longer needs to know where the bean comes from or how it’s created. It follows the pattern of “don’t call me, we’ll call you.” The control of those dependencies is no longer handled by the component but managed by Spring IoC.

This approach reduces coupling and makes it easier to connect application components, making the application more reusable.

Spring IoC in Action

Spring IoC reads your components and configurations (which can be in XML or Java annotations) and provides a ready-to-go application so the developer can start right away focusing on features that bring more value. It creates all beans at runtime when the application starts.

Spring IoC uses your configurations to set up your beans through properties, providing flexibility over the beans. For example, when setting the payment processor to Stripe:


payment.processor=stripe


This can be easily changed to PayPal without touching a single line of your application component that calls this dependency.

Simplified Dependency Usage

Using these dependencies becomes straightforward:

@Component
public class CheckoutService {

    @Autowired
    private PaymentProcessor paymentProcessor;

    public void processCheckout(PaymentDetails details) {
        paymentProcessor.processPayment(details);
    }
}


Flexibility and Productivity

Depending on the changes in your configuration properties (if it does not involve infrastructure changes like changing your database driver), you may not need to recompile and restart your application. Spring can reload the configurations while the application is running.

Conclusion

Spring IoC makes our lives easier, our applications less coupled and more reusable, and enhances productivity by allowing developers to focus on what matters most.

If you like Spring make sure to follow me and stay tuned to learn more! Don’t miss out!

Willian Moya (@WillianFMoya) / X (twitter.com)

Willian Ferreira Moya | LinkedIn

Leave a Reply

Your email address will not be published. Required fields are marked *