IOC Section
1. Spring Ecosystem Overview
Spring is a comprehensive ecosystem providing essential infrastructure for Java application development. The Spring ecosystem typically consists of:
- Spring Framework - the core framework
- Spring Boot - adds auto-configuration capability, which reads Spring Boot Starter configuration during startup, initializes resources, and injects them into the Spring container
- Spring MVC - web framework built on Spring Framework
- Spring Cloud - distributed solution based on Spring Boot for microservices architecture
Spring is a lightweight, open-source framework featuring two core concepts: Inversion of Control (IOC) and Aspect-Oriented Programming (AOP).
2. Understanding Inversion of Control
Core Concept: Instead of creating objects manually via new, object creation is delegated to the Spring IOC container, which manages the entire lifecycle.
Benefits: Significantly reduces coupling between objects.
Implementation Details: The underlying mechanism combines XML configuration, reflection, and the simple factory pattern. Spring reads bean definitions (bean id and class path) from XML configuration files, uses reflection to instantiate objects, and exposes simple factory methods for retrieval.
3. Two IOC Implementations
| Interface | Characteristics | Usage |
|---|---|---|
| BeanFactory | Basic implementation, lazy loading (objects created only when requested) | Rarely used directly by developers |
| ApplicationContext | Extends BeanFactory, eager loading (objects created during container initialization) | Primary interface for application development |
ApplicationContext provides faster response times after startup, while BeanFactory offers slower startup but lower initial resource consumption.
4. Bean Management Approaches
Bean management involves two key operations: object creation and property injection (Dependency Injection).
XML-Based Configuration
Object Creation: Define beans in XML using <bean> element.
Property Injection:
- Constructor injection
- Setter injection
- Can inject simple values or references to other beans
- Supports nested beans and external beans
Annotation-Based Configuration
Object Creation Annotations:
@Component - generic component
@Service - service layer
@Repository - persistence layer
@Controller - web layer
Enable component scanning in XML:
<context:component-scan base-package="com.example"/>
Property Injection Annotations:
@Autowired - by type auto-wiring
@Qualifier - specify bean name for injection
@Resource - by name auto-wiring
@Value - inject simple values
5. Relationship Between DI and IOC
IOC defines the philosophy of delegating object creation to the container. DI (Dependency Injection) is the mechanism that implements IOC by using reflection to create objects and inject their dependencies. Essentially, IOC is the design principle, while DI is the concrete implementation technique.
6. Bean Scopes
- singleton (default) - one instance per Spring container
- prototype - new instance created each time the bean is requested
- request - one instance per HTTP request (web-aware contexts only)
- session - one instance per HTTP session (web-aware contexts only)
7. Singleton Thread Safety
Stateless beans (beans without mutable instance fields) are inherently thread-safe and should use singleton scope.
For stateful beans, singleton scope introduces thread safety concerns. Solutions include:
- Using prototype scope
- Using
ThreadLocalvariables to maintain thread-specific state within singleton beans
8. Spring Bean Lifecycle
The complete lifecycle consists of:
Instantiation → Property Population → Initialization → Destruction
Detailed phases:
- BeanFactoryPostProcessor - modifies bean definitions before instantiation
- InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
- Object Instantiation
- InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
- Property Injection
- Aware Interface Callbacks:
- BeanNameAware.setBeanName()
- BeanFactoryAware.setBeanFactory()
- ApplicationContextAware.setApplicationContext()
- BeanPostProcessor.postProcessBeforeInitialization()
- InitializingBean.afterPropertiesSet()
- Custom init method
- BeanPostProcessor.postProcessAfterInitialization()
- DisposableBean.destroy()
- Custom destroy method
9. Circular Dependency Resolution
Spring resolves circular dependencies using a three-level cache mechanism:
| Cache Level | Content | Purpose |
|---|---|---|
| Level 1 (singletonObjects) | Fully initialized singleton beans | Final storage |
| Level 2 (earlySingletonObjects) | Early reference to beans being instantiated | Break circular references |
| Level 3 (singletonFactories) | ObjectFactory for creating early references | Support AOP proxy creation |
Scenario 1 (No Circular Dependency): Use only Level 1 cache.
Scenario 2 (Circular Dependency Without AOP): Use Level 1 and Level 2 caches. The Level 2 cache holds semi-initialized beans temporarily.
Scenario 3 (Circular Dependency With AOP): All three levels are required. Level 3 stores factories that can generate proxy objects, enabling proper circular dependency resolution while maintaining AOP proxy functionality.
AOP Section
1. Aspect-Oriented Programming
AOP separates cross-cutting concerns from business logic, enabling:
- Transaction management
- Logging
- Security checks
- Other cross-cutting operations
This separation reduces code duplication and decreases coupling between business logic and infrastructure concerns.
2. Static vs Dynamic Proxy
Static Proxy
A proxy class is created at compile time, wrapping the target object. The proxy implements the same interface and can add behavior before/after method execution. Limitations include:
- One proxy class per target class
- Code must be modified when interfaces change
Dynamic Proxy
Proxy classes are generated at runtime.
JDK-Based Dynamic Proxy:
- Requires target classes to implement interfaces
- Uses
java.lang.reflect.ProxyandInvocationHandler - Three parameters: class loader, interfaces, and InvocationHandler implementation
CGLIB Dynamic Proxy:
- Works with classes without interfaces
- Uses ASM library to modify bytecode and generate subclasses
- Cannot proxy final classes or methods (final modifier prevents subclassing)
- Implements
MethodInterceptorinterface
Comparison:
- CGLIB: slower proxy creation, faster invocation
- JDK Proxy: faster creation, slightly slower invocation
- Spring defaults to JDK proxy but switches to CGLIB when classes don't implement interfaces
3. Design Patterns in Spring
| Pattern | Application |
|---|---|
| Simple Factory | IOC container bean creation |
| Singleton | Default bean scope |
| Dynamic Proxy | AOP implementation |
| Template Method | JdbcTemplate, RestTemplate |
| Observer | ApplicationEvent mechanism |
4. AOP Implementation
Key Terminology
- Join Point: Methods that can be intercepted (execution point)
- Pointcut: Actually intercepted methods (the subset of join points)
- Advice: The additional logic to execute (your custom code)
- Aspect: The combination of pointcut and advice (the weaving configuration)
- Weaving: The process of applying advice to target methods
Advice Types
| Type | Annotation | Timing |
|---|---|---|
| Before | @Before | Before method execution |
| After Returning | @AfterReturning | After successful method execution |
| After Throwing | @AfterThrowing | When exception occurs |
| After (Finally) | @After | Always after method execution |
| Around | @Around | Wraps method execution |
Implementation Steps
Step 1: Add Maven Dependency
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9</version>
</dependency>
Step 2: XML Configuration Approach
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:pointcut id="servicePointcut"
expression="execution(* com.example.service.*.*(..))"/>
<aop:advisor advice-ref="loggingAdvice" pointcut-ref="servicePointcut"/>
</aop:config>
</beans>
Step 3: Advice Implementation Class
public class LoggingAdvice implements
MethodBeforeAdvice,
AfterReturningAdvice,
MethodInterceptor {
@Override
public void before(Method method, Object[] args, Object target) {
System.out.println("[BEFORE] Executing: " + method.getName());
}
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) {
System.out.println("[AFTER] Completed: " + method.getName());
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("[AROUND-BEFORE] Method: " +
invocation.getMethod().getName());
Object result = invocation.proceed();
System.out.println("[AROUND-AFTER] Result processed");
return result;
}
}
Annotation Configuration Approach
Enable AspectJ Auto-Proxy:
<aop:aspectj-autoproxy/>
Aspect Class Implementation:
@Aspect
@Component
public class PerformanceMonitor {
@Pointcut("execution(* com.example.service..*(..))")
public void serviceMethods() {}
@Before("serviceMethods()")
public void preCheck(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("[PRE] Entering method: " + methodName);
}
@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void postCheck(Object result) {
System.out.println("[POST] Method returned: " + result);
}
@Around("serviceMethods()")
public Object performanceTrack(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
Object outcome = pjp.proceed();
long duration = System.currentTimeMillis() - startTime;
System.out.println("[TIMING] Execution time: " + duration + "ms");
return outcome;
}
}
5. Spring Transaction Management
Programmatic Transaction Management
Direct use of TransactionTemplate or PlatformTransactionManager. Provides fine-grained control but introduces code coupling.
transactionTemplate.execute(status -> {
// business logic
return result;
});
Declarative Transaction Management
Uses AOP to wrap methods with transaction logic. Configuration is externalized and non-invasive to business code.
<tx:advice id="transactionAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="transfer*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
Or via annotation:
@Transactional(propagation = Propagation.REQUIRED)
public void transfer(Account from, Account to, BigDecimal amount) {
// transfer logic
}
Comparison
Declarative approach is preferred for most scenarios due to its non-invasive nature. However, it operates at method granularity. For finer control (code-block level), programmatic management or method extraction refactoring is necessary.
6. Transaction Propagation Behaviors
Transaction propagation determines behavior when a transaction method calls another transaction method.
| Propagation | Behavior |
|---|---|
| REQUIRED (default) | Joins existing transaction or creates new one |
| REQUIRES_NEW | Always creates new transaction, suspends outer transaction |
| SUPPORTS | Participates in transaction if exists, otherwise executes non-transactionally |
| NOT_SUPPORTED | Executes non-transactionally, suspends outer transaction if exists |
| NEVER | Executes non-transactionally, fails if transaction exists |
| MANDATORY | Requires existing transaction, fails if none exists |
| NESTED | Uses savepoints for nested transactions within outer transaction |
The NESTED propagation allows partial rollback using savepoints—inner transaction failures don't necessarily roll back the outer transaction if the exception is caught and handled appropriately.