Skip to content

Kunal70616c/spring-modern-ioc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Spring Modern IoC & Dependency Injection

A comprehensive demonstration of Spring Framework's evolution from XML-based to modern annotation-based configuration. This repository showcases three different approaches to Inversion of Control (IoC) and Dependency Injection (DI), making it an excellent learning resource for understanding Spring's configuration methods.

📋 Table of Contents

Overview

This project demonstrates the evolution of Spring IoC configuration through three distinct approaches:

  1. Pure XML Configuration - Traditional Spring approach (pre-Spring 3.0)
  2. Hybrid XML + Annotations - Transition phase combining both methods
  3. Pure Java Configuration - Modern Spring approach (Spring 3.0+)

Each approach achieves the same goal: letting Spring manage object creation and dependency injection, but with different configuration styles.

Key Concepts Explained

🔄 Inversion of Control (IoC)

What is IoC?

  • Traditional Programming: Your code controls object creation using new keyword
  • IoC Pattern: Framework (Spring) controls object creation and lifecycle
  • Benefit: Loose coupling, easier testing, centralized configuration

Example:

// Without IoC
Customer customer = new Customer();
FullName fullName = new FullName();
customer.setFullName(fullName); // Manual wiring

// With IoC
Customer customer = context.getBean(Customer.class); // Spring creates and wires everything

💉 Dependency Injection (DI)

What is DI?

  • A design pattern where objects receive their dependencies from external sources
  • Spring "injects" dependencies rather than objects creating them
  • Makes code more testable and maintainable

Types Demonstrated:

  1. Constructor Injection - Dependencies passed through constructor (recommended)
  2. Setter Injection - Dependencies set through setter methods
  3. Field Injection - Dependencies injected directly into fields using @Autowired

🏭 ApplicationContext

What is ApplicationContext?

  • Advanced IoC container (extends BeanFactory)
  • Manages complete lifecycle of beans
  • Provides additional enterprise features (event propagation, declarative mechanisms)

Two implementations shown:

  • ClassPathXmlApplicationContext - Loads configuration from XML files
  • AnnotationConfigApplicationContext - Loads configuration from Java classes

Three Approaches Demonstrated

1️⃣ Pure XML Configuration (CustomerApp)

Traditional approach where all beans are defined in XML files.

ApplicationContext context = new ClassPathXmlApplicationContext("banking-app.xml");
Customer customer = context.getBean("customer", Customer.class);

Configuration: banking-app.xml

<bean id="customer" class="sh.surge.kunal.banking.models.Customer">
    <property name="accountNo" value="6855858"/>
    <property name="email" value="[email protected]"/>
</bean>

Use Case: Legacy applications, XML-heavy environments

2️⃣ Hybrid: XML + Annotations (UserApp)

Transition approach combining XML for container setup with annotations for bean definition.

ApplicationContext context = new ClassPathXmlApplicationContext("banking-app.xml");
UserDataDao userDataDao = context.getBean(UserDataDao.class);

XML enables component scanning:

<context:component-scan base-package="sh.surge.kunal.banking.*"/>

Classes use annotations:

@Component
@PropertySource("classpath:application.properties")
public class UserDataDao {
    @Autowired
    private RestClient restClient;
    
    @Value("${userUrl}")
    private String userDataUrl;
}

Use Case: Migrating from XML to annotations, mixed codebases

3️⃣ Pure Java Configuration (TransactionApp)

Modern approach with zero XML - everything configured using Java annotations.

ApplicationContext context = new AnnotationConfigApplicationContext(TransactionConfigurer.class);
TransactionService service = context.getBean(TransactionService.class);

Configuration class:

@Configuration
@ComponentScan(basePackages = "sh.surge.kunal.banking")
@Import({DirectDebitConfigurer.class, ExternalDebitConfigurer.class})
public class TransactionConfigurer {
}

Use Case: Modern Spring applications, Spring Boot (default approach)

Getting Started

Prerequisites

  • Java: JDK 8 or higher
  • IDE: IntelliJ IDEA (Community or Ultimate)
  • Build Tool: Maven
  • Git: For cloning the repository

Clone and Setup

Step 1: Clone the Repository

# Clone the repository
git clone https://github.com/Kunal70616c/spring-modern-ioc.git

# Navigate to project directory
cd spring-modern-ioc

Step 2: Import in IntelliJ IDEA

Method 1: From Welcome Screen

  1. Open IntelliJ IDEA
  2. Click "Open"
  3. Navigate to the cloned spring-modern-ioc folder
  4. Select the project root (containing pom.xml)
  5. Click "OK"
  6. IntelliJ will auto-detect Maven and import dependencies

Method 2: From File Menu

  1. File → Open
  2. Select the project folder
  3. Click "OK"
  4. Wait for Maven to sync (check progress bar at bottom)

Step 3: Configure Project SDK

  1. File → Project Structure (Ctrl+Alt+Shift+S / Cmd+; on Mac)
  2. Project Settings → Project
  3. Set Project SDK to Java 8+
  4. Set Language level to 8+
  5. Click "Apply" and "OK"

Step 4: Maven Sync

  1. Open Maven tab (right sidebar)
  2. Click Reload icon (circular arrows)
  3. Wait for dependency download to complete

Step 5: Verify Resources

Ensure these files exist:

  • src/main/resources/banking-app.xml
  • src/main/resources/application.properties

If the resources folder isn't marked as Resources Root:

  • Right-click src/main/resources
  • Mark Directory as → Resources Root

Maven Dependencies

Your pom.xml should include:

<dependencies>
    <!-- Spring Context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.1.x</version>
    </dependency>
    
    <!-- Spring WebFlux (for RestClient) -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webflux</artifactId>
        <version>6.1.x</version>
    </dependency>
    
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.30</version>
    </dependency>
    
    <!-- JavaFaker -->
    <dependency>
        <groupId>com.github.javafaker</groupId>
        <artifactId>javafaker</artifactId>
        <version>1.0.2</version>
    </dependency>
    
    <!-- JSON -->
    <dependency>
        <groupId>org.json</groupId>
        <artifactId>json</artifactId>
        <version>20231013</version>
    </dependency>
</dependencies>

Project Structure

spring-modern-ioc/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── sh/surge/kunal/banking/
│   │   │       ├── configurations/          # Configuration classes
│   │   │       │   ├── DirectDebitConfigurer.java
│   │   │       │   ├── ExternalDebitConfigurer.java
│   │   │       │   ├── RestConfigurer.java
│   │   │       │   └── TransactionConfigurer.java
│   │   │       ├── facades/                  # Facade pattern interfaces
│   │   │       │   └── Account.java
│   │   │       ├── models/                   # Domain models
│   │   │       │   ├── AbstractAccount.java
│   │   │       │   ├── Address.java
│   │   │       │   ├── CompanyAddress.java
│   │   │       │   ├── CompanyType.java (Enum)
│   │   │       │   ├── Corporate.java
│   │   │       │   ├── CurrentAccount.java
│   │   │       │   ├── Customer.java
│   │   │       │   ├── DirectDebitTransaction.java
│   │   │       │   ├── ExternalDebitTransaction.java
│   │   │       │   ├── FullName.java
│   │   │       │   ├── Gender.java (Enum)
│   │   │       │   ├── HomeAddress.java
│   │   │       │   ├── Individual.java
│   │   │       │   ├── SavingsAccount.java
│   │   │       │   └── Transaction.java (Abstract)
│   │   │       ├── services/                 # Service layer
│   │   │       │   └── TransactionService.java
│   │   │       └── utils/                    # Application entry points
│   │   │           ├── CustomerApp.java      # Pure XML approach
│   │   │           ├── TransactionApp.java   # Pure Java Config approach
│   │   │           ├── UserApp.java          # Hybrid approach
│   │   │           └── UserDataDao.java      # REST client demo
│   │   └── resources/
│   │       ├── application.properties        # External configuration
│   │       └── banking-app.xml              # XML bean definitions
│   └── test/
│       └── java/
├── pom.xml                                   # Maven configuration
└── README.md

Approach Comparison

Aspect Pure XML Hybrid (XML + Annotations) Pure Java Config
Container Initialization ClassPathXmlApplicationContext ClassPathXmlApplicationContext AnnotationConfigApplicationContext
Bean Definition <bean> tags in XML @Component, @Service annotations @Bean methods + @Component
Configuration Location banking-app.xml XML + annotated classes @Configuration classes
Dependency Injection <property> / <constructor-arg> @Autowired, @Qualifier @Autowired, Constructor injection
Component Scanning Manual in XML <context:component-scan> @ComponentScan
External Properties Not shown @PropertySource + @Value @PropertySource + @Value
Refactoring Support ❌ Poor (no IDE support) ⚠️ Partial ✅ Excellent (type-safe)
Verbosity High (lots of XML) Medium Low (concise)
Learning Curve Easy to start Medium Steeper initially
Modern Usage Legacy only Migration phase ✅ Recommended
Example App CustomerApp UserApp + UserDataDao TransactionApp

Code Deep Dive

Approach 1: Pure XML Configuration (CustomerApp)

CustomerApp.java

public class CustomerApp {
    public static void main(String[] args) {
        Faker faker = new Faker();
        Logger logger = Logger.getLogger("CustomerApp");
        
        // Load context from XML file
        ApplicationContext context = new ClassPathXmlApplicationContext("banking-app.xml");
        
        // IoC: Spring creates the object
        FullName fullName1 = context.getBean("fullName", FullName.class);
        
        // DI: Set properties (Faker generates random data)
        fullName1.setFirstName(faker.name().firstName());
        fullName1.setLastName(faker.name().lastName());
        
        logger.info("Full Name: " + fullName1);
        
        // Get Customer bean (with dependencies already injected by Spring)
        Customer customer1 = context.getBean("customer", Customer.class);
        customer1.setAccountNo(faker.number().numberBetween(10000000, 99999999));
        
        logger.info("Customer: " + customer1);
    }
}

Key Points:

  • Uses ClassPathXmlApplicationContext to load XML configuration
  • All bean definitions are in banking-app.xml
  • Manual property setting after bean retrieval
  • Good for understanding Spring basics

banking-app.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- Enable annotation processing -->
    <context:annotation-config/>
    
    <!-- Scan for components with annotations -->
    <context:component-scan base-package="sh.surge.kunal.banking.*"/>
</beans>

Configuration Elements:

  • <context:annotation-config> - Enables @Autowired, @Required, etc.
  • <context:component-scan> - Scans packages for @Component, @Service, etc.

Approach 2: Hybrid (UserApp + UserDataDao)

UserApp.java

public class UserApp {
    public static void main(String[] args) {
        Logger logger = Logger.getLogger("UserApp");
        
        // XML loads the context BUT enables component scanning
        ApplicationContext context = new ClassPathXmlApplicationContext("banking-app.xml");
        
        // Spring finds UserDataDao via @Component annotation
        UserDataDao userDataDao = context.getBean(UserDataDao.class);
        
        // Display user data fetched from REST API
        userDataDao.getUserData().entrySet().stream()
            .map(entry -> entry.getKey() + " : " + entry.getValue())
            .forEach(logger::info);
    }
}

UserDataDao.java (The Magic Happens Here!)

@Component  // Marks this class as a Spring-managed bean
@PropertySource("classpath:application.properties")  // Loads external properties
public class UserDataDao {
    
    @Autowired  // Spring injects RestClient automatically
    private RestClient restClient;
    
    @Value("${userUrl}")  // Injects value from application.properties
    private String userDataUrl;
    
    private Map<String, String> userDataMap;
    
    public Map<String, String> getUserData() {
        userDataMap = new HashMap<>();
        
        // Fetch data from external API using injected RestClient
        String response = restClient.get()
            .uri(userDataUrl)  // URL from properties file
            .retrieve()
            .body(String.class);
        
        // Parse JSON response
        JSONArray usersArray = new JSONArray(response);
        for (int i = 0; i < usersArray.length(); i++) {
            String username = usersArray.getJSONObject(i).getString("name");
            String email = usersArray.getJSONObject(i).getString("email");
            userDataMap.put(username, email);
        }
        
        return userDataMap;
    }
}

Key Annotations Explained:

Annotation Purpose Example
@Component Marks class as Spring bean Auto-discovered during component scan
@Autowired Injects dependency Spring finds RestClient bean and injects it
@Value("${key}") Injects property value Reads userUrl from application.properties
@PropertySource Loads properties file Makes application.properties available

application.properties

userUrl=https://jsonplaceholder.typicode.com/users

Benefits of this approach:

  • Externalizes configuration (easier to change environments)
  • Combines XML stability with annotation convenience
  • Good migration path from pure XML

Approach 3: Pure Java Configuration (TransactionApp)

TransactionApp.java

public class TransactionApp {
    public static void main(String[] args) {
        Logger logger = Logger.getLogger("TransactionApp");
        
        // Zero XML! Load configuration from Java class
        ApplicationContext context = 
            new AnnotationConfigApplicationContext(TransactionConfigurer.class);
        
        // Get service with all dependencies injected
        TransactionService transactionService = context.getBean(TransactionService.class);
        
        // Process transactions
        logger.info("Direct Debit: " + transactionService.processDirectDebit());
        logger.info("External Debit: " + transactionService.processExternalDebit());
    }
}

TransactionConfigurer.java

@Configuration  // Marks this as a configuration class (replaces XML)
@ComponentScan(basePackages = "sh.surge.kunal.banking")  // Scans for components
@Import({DirectDebitConfigurer.class, ExternalDebitConfigurer.class})  // Imports other configs
public class TransactionConfigurer {
    // This class doesn't need methods - it just enables component scanning
    // Actual beans are defined in imported configurers or found via @Component
}

Why @Import?

  • Modularizes configuration
  • Separates concerns (each configurer handles specific beans)
  • Makes large applications manageable

DirectDebitConfigurer.java

@Configuration
public class DirectDebitConfigurer {
    
    @Bean(name = "directDebitTransaction")  // Creates a Spring-managed bean
    public Transaction getDirectDebitTransaction() {
        return new DirectDebitTransaction();  // Factory method
    }
}

ExternalDebitConfigurer.java

@Configuration
public class ExternalDebitConfigurer {
    
    @Bean(name = "externalDebitTransaction")
    public Transaction getExternalDebitTransaction() {
        return new ExternalDebitTransaction();
    }
}

RestConfigurer.java

@Configuration
public class RestConfigurer {
    
    @Bean  // Creates RestClient bean for HTTP calls
    public RestClient restClient() {
        return RestClient.create();  // Spring WebClient factory method
    }
}

TransactionService.java (Constructor Injection - Best Practice!)

@Service  // Specialized @Component for service layer
@Data  // Lombok: generates getters, setters, toString, etc.
public class TransactionService {
    
    private final Transaction directDebitTransaction;
    private final Transaction externalDebitTransaction;
    private Faker faker;
    
    // Constructor Injection (recommended over field injection)
    public TransactionService(
        @Qualifier("directDebitTransaction") Transaction directDebitTransaction,
        @Qualifier("externalDebitTransaction") Transaction externalDebitTransaction
    ) {
        this.directDebitTransaction = directDebitTransaction;
        this.externalDebitTransaction = externalDebitTransaction;
        this.faker = new Faker();
    }
    
    public Transaction processDirectDebit() {
        DirectDebitTransaction transaction = (DirectDebitTransaction) this.directDebitTransaction;
        
        // Use Faker to generate realistic test data
        transaction.setSenderName(faker.name().fullName());
        transaction.setReceiverName(faker.name().fullName());
        transaction.setTransactionDate(faker.date().birthday()
            .toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
        transaction.setAmount(faker.number().numberBetween(10000, 100000));
        transaction.setPaymentDate(faker.date().birthday()
            .toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
        
        return transaction;
    }
    
    public Transaction processExternalDebit() {
        ExternalDebitTransaction transaction = (ExternalDebitTransaction) this.externalDebitTransaction;
        
        transaction.setSenderName(faker.name().fullName());
        transaction.setReceiverName(faker.name().fullName());
        transaction.setTransactionDate(faker.date().birthday()
            .toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
        transaction.setAmount(faker.number().numberBetween(10000, 100000));
        transaction.setBranchAddress(faker.address().fullAddress());
        transaction.setBranchCode(faker.number().digits(5).toString());
        transaction.setBranchPostalCode(faker.address().zipCode());
        transaction.setBranchName(faker.company().name());
        
        return transaction;
    }
}

Key Points:

  • @Service is a specialized @Component for service layer
  • Constructor injection is preferred (immutable, testable)
  • @Qualifier disambiguates when multiple beans of same type exist
  • final fields ensure immutability (dependencies can't be changed)

Model Classes with Lombok

Customer.java

@Data  // Generates getters, setters, toString, equals, hashCode
@AllArgsConstructor  // Constructor with all fields
@NoArgsConstructor   // No-arg constructor (required by Spring)
@Component  // Makes this a Spring-managed bean
public class Customer {
    protected long accountNo;
    
    @Autowired
    protected FullName fullName;
    
    @Autowired
    @Qualifier("homeAddress")  // Specify which Address bean to inject
    protected Address address;
    
    @Autowired
    @Qualifier("savingsAccount")
    protected Account account;
    
    protected long contactNo;
    protected String email;
    protected String password;
}

Lombok Annotations:

  • @Data - Reduces boilerplate (no need to write getters/setters)
  • @AllArgsConstructor / @NoArgsConstructor - Constructor generation
  • Makes code cleaner and more maintainable

DirectDebitTransaction.java

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)  // Uses parent class fields in equals/hashCode
@ToString(callSuper = true)  // Includes parent class toString
public class DirectDebitTransaction extends Transaction {
    private LocalDate paymentDate;
}

ExternalDebitTransaction.java

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class ExternalDebitTransaction extends Transaction {
    private String branchName;
    private String branchAddress;
    private String branchPostalCode;
    private String branchCode;
}

Running the Applications

Option 1: Run Directly from IntelliJ

Run CustomerApp (Pure XML)

  1. Open CustomerApp.java
  2. Right-click in the editor
  3. Select "Run 'CustomerApp.main()'"
  4. View output in Run console

Expected Output:

INFO: Full Name: FullName(firstName=John, middleName=Michael, lastName=Doe)
INFO: Address 1: HomeAddress(doorNo=123, streetName=Main Street, city=New York, pinCode=100001)
INFO: Customer: Customer(accountNo=12345678, fullName=..., [email protected], ...)

Run UserApp (Hybrid)

  1. Open UserApp.java
  2. Right-click → "Run 'UserApp.main()'"
  3. Application fetches user data from REST API

Expected Output:

INFO: Leanne Graham : [email protected]
INFO: Ervin Howell : [email protected]
INFO: Clementine Bauch : [email protected]
...

Run TransactionApp (Pure Java Config)

  1. Open TransactionApp.java
  2. Right-click → "Run 'TransactionApp.main()'"

Expected Output:

INFO: Direct Debit: DirectDebitTransaction(paymentDate=2024-03-15, senderName=Alice Smith, receiverName=Bob Johnson, amount=45000)
INFO: External Debit: ExternalDebitTransaction(branchName=Chase Bank, branchAddress=123 Wall St, amount=75000)

Option 2: Run Using Maven

Open Terminal in IntelliJ (Alt+F12 / Option+F12):

# Compile the project
mvn clean compile

# Run CustomerApp (Pure XML)
mvn exec:java -Dexec.mainClass="sh.surge.kunal.banking.utils.CustomerApp"

# Run UserApp (Hybrid)
mvn exec:java -Dexec.mainClass="sh.surge.kunal.banking.utils.UserApp"

# Run TransactionApp (Pure Java Config)
mvn exec:java -Dexec.mainClass="sh.surge.kunal.banking.utils.TransactionApp"

Option 3: Create Run Configurations

For each application:

  1. Run → Edit Configurations
  2. Click +Application
  3. Configure:
    • Name: CustomerApp (or relevant name)
    • Main class: sh.surge.kunal.banking.utils.CustomerApp
    • Module: Select your project module
  4. ApplyOK
  5. Select from dropdown and click Run (green play icon)

Create configurations for all three apps to easily switch between them!

Troubleshooting

Issue: Dependencies not downloading

Solution:

  • Check internet connection
  • File → Invalidate Caches → Invalidate and Restart
  • Delete .m2/repository cache and re-sync

Issue: "Cannot find banking-app.xml"

Solution:

  • Verify file is in src/main/resources/
  • Right-click resources folder → Mark Directory as → Resources Root
  • Rebuild: Build → Rebuild Project

Issue: "No bean named 'X' available"

Solution:

  • For XML approach: Check bean definition in banking-app.xml
  • For annotation approach: Ensure @Component or @Bean is present
  • Verify component scan package matches your package structure

Issue: RestClient bean not found (UserApp)

Solution:

  • Ensure RestConfigurer is in a scanned package
  • Verify Spring WebFlux dependency is in pom.xml
  • Check @Configuration annotation is present on RestConfigurer

Issue: Application hangs or no output

Solution:

  • Check for infinite loops in bean creation
  • Verify circular dependencies don't exist
  • Enable debug logging: Add -Dlogging.level.org.springframework=DEBUG to VM options

Learning Outcomes

After studying this repository, you will understand:

Core Spring Concepts

Inversion of Control (IoC) - How Spring manages object lifecycle
Dependency Injection (DI) - Constructor, setter, and field injection
ApplicationContext - Spring's advanced IoC container
Bean Lifecycle - Creation, initialization, and destruction
Bean Scopes - Singleton (default), prototype, etc.

Configuration Methods

XML Configuration - Traditional <bean> definitions
Annotation-based Config - @Component, @Service, @Repository, @Controller
Java-based Config - @Configuration and @Bean methods
Hybrid Approach - Combining XML and annotations
Component Scanning - @ComponentScan and <context:component-scan>

Advanced Features

Property Injection - @Value and @PropertySource
Qualifier Usage - @Qualifier for disambiguation
Configuration Import - @Import for modular configs
REST Integration - Using RestClient in Spring
Lombok Integration - Reducing boilerplate code
JavaFaker Integration - Generating test data

Best Practices

Constructor Injection - Preferred over field injection
Immutability - Using final fields with constructor injection
Separation of Concerns - Configuration, services, and models
External Configuration - Using application.properties
Type Safety - Java config provides compile-time checking

Key Takeaways

When to Use Each Approach?

Use Pure XML When:

  • Working with legacy Spring applications (pre-3.0)
  • Team requires centralized, visual configuration
  • Integrating with XML-heavy enterprise systems

Use Hybrid (XML + Annotations) When:

  • Migrating from XML to modern Spring
  • Need gradual transition with minimal risk
  • Some configuration must remain in XML (legacy constraints)

Use Pure Java Config When:

  • Starting new Spring projects ✅ RECOMMENDED
  • Using Spring Boot (this is the default)
  • Want type-safe, refactoring-friendly configuration
  • Need IDE support (autocomplete, navigation)

Evolution Timeline

Spring 1.x (2004)     Spring 2.5 (2008)      Spring 3.0 (2009)        Spring 5.x+ (2017+)
     │                      │                      │                          │
     │                      │                      │                          │
  Pure XML          XML + Annotations       Java Config            Spring Boot
  <bean>            @Component              @Configuration          Auto-configuration
  <property>        @Autowired              @Bean                   Convention over config
                    @Service                @ComponentScan          Zero XML by default

Why Modern Spring Recommends Annotations?

  1. Type Safety - Compile-time checking, refactoring support
  2. Less Verbose - No need to repeat class names in XML
  3. Better IDE Support - Navigate to bean definitions easily
  4. Testability - Constructor injection makes unit testing easier
  5. Maintainability - Configuration close to the code it configures

Additional Resources

Official Documentation

Related Technologies Used

Next Steps

After mastering this repository:

  1. Spring Boot - Modern, opinionated framework built on Spring
  2. Spring Data JPA - Database access with repositories
  3. Spring MVC - Build web applications
  4. Spring Security - Authentication and authorization
  5. Spring AOP - Aspect-oriented programming for cross-cutting concerns

🌟 Star & Support

If this guide helped you understand the evolution of Spring IoC, please consider giving it a Star! It helps others discover the repository and stay updated with new examples.

  • Star this Repo: Click the ⭐ button at the top right of this page.
  • Follow for More: Follow me on GitHub to keep up with my latest Java and Spring projects.
  • Share: If you know someone learning Spring, feel free to share this resource!

Happy Coding! 🚀


About

A modern demonstration of Spring IoC and Dependency Injection using annotation-based configuration (@component, @Autowired, @configuration) - the contemporary alternative to XML-based bean management.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages