๐ชด 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 (
newkeyword 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@RequestMappingwith specific methods.@Consumes– Defines accepted media types (e.g., JSON, XML). POJOs can use@XmlRootElementfor 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=devinapplication.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
@Transactionalat 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
Post a Comment