๐Ÿชด Spring Framework Essentials: Core Concepts, Beans, and Dependency Injection

Welcome back to The Code Hut! ☕ This post dives into the core building blocks of the Spring Framework — Dependency Injection (DI), Bean scopes, AOP, Transaction Management, and more — the foundation of most modern Java applications. ๐Ÿš€

1. ๐ŸŒฑ What Is Spring Framework?

The Spring Framework provides a comprehensive programming and configuration model for Java-based enterprise applications. It promotes loose coupling through Dependency Injection (DI) and supports a modular architecture.

  • Dependency Injection (DI): The Spring container manages object creation and wiring automatically.
  • Modules: Core Container, Data access/integration (JDBC, JPA...), Web, Test, AOP, Messaging, etc.
  • Benefits: Clean architecture, easy testing, and maintainable code.

๐Ÿ”„ Inversion of Control (IoC) & the Hollywood Principle

At the heart of the Spring Framework lies Inversion of Control (IoC). Instead of application code creating and managing its own dependencies, this responsibility is inverted and delegated to the Spring container.

  • Traditional approach: Classes control the creation and lifecycle of their dependencies (new keyword everywhere).
  • Spring IoC: The container creates, configures, and injects dependencies automatically.

This leads directly to the Hollywood Principle:

"Don’t call us, we’ll call you."

In Spring applications, your code does not control the application flow directly. Instead, you declare intent (via annotations or configuration), and the framework invokes your components at the right time:

  • Controllers are called by the web framework
  • Services are injected where needed
  • Lifecycle hooks (@PostConstruct, runners, listeners) are triggered by Spring

Together, IoC + the Hollywood Principle enable: loose coupling, high testability, and framework-driven execution — core characteristics of modern Spring applications.

2. ๐Ÿงฉ Spring Beans & Containers

Objects managed by Spring are called Beans. They are created, configured, and managed by the Spring IoC container.

  • BeanFactory: The simplest container providing basic DI.
  • ApplicationContext: Extends BeanFactory with features like event propagation and internationalization.

๐Ÿ’ก Bean Scopes

  • singleton: One instance per container (default)
  • prototype: New instance per request
  • request, session, global-session: Web application scopes

// Example: Bean definition with different scopes
@Component
@Scope("prototype")
public class UserService {
    public void register(String name) { /* ... */ }
}

3. ๐Ÿ”„ Bean Lifecycle & Startup Hooks

Spring provides several hooks that allow you to execute logic at specific moments during application startup and bean initialization.

๐ŸŒฑ @PostConstruct

@PostConstruct is a lifecycle callback invoked after dependency injection but before the application is ready to serve requests.

  • Defined by JSR-250
  • Runs once per bean instance
  • Ideal for validation or initialization logic

@Component
public class CacheInitializer {

    @PostConstruct
    public void init() {
        System.out.println("Cache initialized");
    }
}

๐Ÿš€ CommandLineRunner

CommandLineRunner runs after the Spring context is fully initialized. It receives raw command-line arguments.


@Component
public class StartupRunner implements CommandLineRunner {

    @Override
    public void run(String... args) {
        System.out.println("Application started with args: " + Arrays.toString(args));
    }
}

๐Ÿ“ฆ ApplicationRunner

ApplicationRunner is similar to CommandLineRunner but provides structured access to application arguments.


@Component
public class AppStartupRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        if (args.containsOption("init")) {
            System.out.println("Initialization flag detected");
        }
    }
}

๐Ÿงญ Execution Order Summary

  • Bean instantiation
  • Dependency injection
  • @PostConstruct
  • Application context ready
  • CommandLineRunner / ApplicationRunner

4. ๐Ÿงฎ Transaction Management (Extended)

Spring simplifies transaction handling through @Transactional, supporting both declarative and programmatic models.

  • Propagation: Determines transaction behavior across methods:
    • REQUIRED – Joins an existing transaction if present, or creates a new one.
    • REQUIRES_NEW – Suspends the current transaction and starts a new one.
    • SUPPORTS – Executes within a transaction if one exists; otherwise, no transaction.
    • MANDATORY – Must run inside an existing transaction; throws exception if none.
    • NEVER – Executes outside a transaction; throws exception if a transaction exists.
    • NOT_SUPPORTED – Suspends any existing transaction and executes non-transactionally.
    • NESTED – Executes within a nested transaction if a transaction exists.
  • Isolation: Defines visibility of uncommitted data and prevents concurrency issues:
    • READ_UNCOMMITTED – Allows dirty reads.
    • READ_COMMITTED – Prevents dirty reads; allows non-repeatable reads.
    • REPEATABLE_READ – Prevents dirty and non-repeatable reads; phantom reads possible.
    • SERIALIZABLE – Full isolation; prevents dirty, non-repeatable, and phantom reads.
  • Common read anomalies: Concurrency issues that can occur depending on the isolation level:
    • Dirty Read – A transaction reads data modified by another transaction that has not yet been committed. If the other transaction rolls back, the read data was never actually valid.
    • Non-repeatable Read – A transaction reads the same row twice and gets different values because another transaction committed an update in between.
    • Phantom Read – A transaction re-executes a query and sees additional or missing rows because another transaction inserted or deleted data.

@Service
public class OrderService {
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public void placeOrder(Order order) {
        orderRepository.save(order);
        paymentService.charge(order);
    }
    @Transactional
    public void processOrder() {
    	// Uses Isolation.DEFAULT → database default isolation level
        // PostgreSQL → READ_COMMITTED
        // MySQL (InnoDB) → REPEATABLE_READ
    }
}

5. ๐ŸŽฏ Aspect-Oriented Programming (AOP)

AOP modularizes cross-cutting concerns such as logging, security, or transaction management. Add additional behaviours without modifying the code.

  • Aspect: Class containing shared logic.
  • Joinpoint: Specific points in code (e.g., method execution).
  • Advice: Code executed before, after, or around method calls.

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Calling: " + joinPoint.getSignature());
    }
}

6. ⚙️ Stereotype Annotations (Extended)

Spring provides several stereotype annotations to simplify component scanning and clarify intent:

  • @Component → Generic component.
  • @Service → Business logic layer.
  • @Repository → Data access layer (translates exceptions).
  • @Controller → Web layer (MVC endpoints).
  • @RestController → RESTful web services (@Controller + @ResponseBody).

๐Ÿ’ก Web Mappings

  • @RequestMapping – Maps HTTP requests: value="/ex/foos", method=GET, headers, produces="application/json"
  • @GetMapping / @PostMapping – Shortcut for @RequestMapping with specific methods.
  • @Consumes – Defines accepted media types (e.g., JSON, XML). POJOs can use @XmlRootElement for XML support.

7. ๐Ÿงฐ Exception Handling

Spring provides flexible exception handling mechanisms for both MVC and REST controllers:

  • @ControllerAdvice – Global exception handler across controllers.
  • @ExceptionHandler – Handle specific exceptions locally.

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public ResponseEntity handleError(Exception ex) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                             .body("Error: " + ex.getMessage());
    }
}

8. ๐ŸŒ Profiles & Configuration

Spring Profiles let you define environment-specific configurations (e.g., dev, test, prod).

  • @Profile("dev") – Load beans only for the specified environment.
  • Activate via spring.profiles.active=dev in application.properties.

@Configuration
@Profile("prod")
public class ProdDatabaseConfig {
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource(...);
    }
}

9. ๐Ÿงพ Summary Table

Concept Purpose Example
Dependency Injection Manage dependencies via Spring container @Autowired / @Inject
Bean Scope Defines lifecycle of Spring beans @Scope("singleton")
Transactional Automatic transaction management @Transactional
AOP Cross-cutting logic (logging, security) @Aspect
Profiles Environment-specific configuration @Profile("dev")
Stereotype Annotations Clarify bean role in app @Component, @Service, @Repository, @Controller, @RestController

10. ๐Ÿš€ Tips for Developers

  • Keep beans small and focused — one responsibility per class.
  • Prefer constructor injection for better testability.
  • Use @Transactional at the service layer, not in repositories.
  • Leverage AOP for logging, auditing, or retry logic instead of duplicating code.
  • Separate configurations using profiles to keep environments clean and modular.

Next in the Series

In the next post, we’ll explore Spring Boot Deep Dive: Auto-Configuration, Starters, and 12-Factor Apps

Labels: Spring, Spring Framework, Dependency Injection, IoC, Beans, AOP, @Transactional, Java, ApplicationContext, Profiles, @Component, @Service, @Repository, @Controller, @RestController, Exception Handling

Comments

Popular posts from this blog

๐Ÿ› ️ The Code Hut - Index

๐Ÿ›ก️ Resilience Patterns in Distributed Systems

๐Ÿ›ก️ Thread-Safe Programming in Java: Locks, Atomic Variables & LongAdder