Spring Framework Core Concepts: IoC, AOP, and Transaction Management

Understanding Spring Framework Architecture

Monolithic Architecture

A single project deployed as a WAR package running on a single Tomcat instance. This approach integrates all functionality into one cohesive unit, often referred to as "all in one."

Key technologies in monolithic architecture typically include:

  • Spring Framework
  • Spring MVC
  • MyBatis

Distributed Architecture

A project organized into multiple modules, where each module runs on its own Tomcat server. Modules can communicate with each other while maintaining internal independence.

Key technologies in distributed architecture include:

  • Spring Boot (with SSM components)
  • Spring Cloud
  • Various middleware solutions

Framework Concept and Benefits

A framework is a software system that integrates foundational components like basic structures, specifications, design patterns, programming languages, and libraries. It addresses common problems in specific domains, enabling developers to achieve software development goals more efficiently and stably.

Key advantages of frameworks:

  • Enhanced Development Efficiency: Pre-built components and tools accelerate development compared to manual coding.
  • Reduced Development Costs: Standardized code snippets eliminate redundant development while providing optimized systems.
  • Improved Application Stability: Thoroughly tested components and design patterns minimize bugs and enhance reliability.
  • Standardized Solutions: Domain-specific frameworks provide common language and conceptual foundations for better team communication.

However, frameworks also present challenges:

  • Learning Curve: Specific languages and paradigms require significant time investment.
  • Potential Limitations: Some specialized requirements may exceed framework capabilities.
  • Version Compatibility Issues: Framework updates can cause compatibility problems requiring careful migration.
  • Architectural Risks: Misunderstanding framwork concepts can lead to design flaws.

Spring Framework Fundamentals

Spring Framework Structure

The Spring Framework consists of several key modules:

Module Description
Core Container Foundation of Spring applications; all functionality requires the IoC container.
AOP & Aspects Provides aspect-oriented programming capabilities.
TX Enables declarative transaction management.
Spring MVC Offers integrated features for web applications.

Spring Framework Advantages

  • Rich Ecosystem: Extensive modules and libraries like Spring Boot, Security, and Cloud.
  • Modular Design: Loosely coupled components enable reusability and maintainability.
  • Simplified Java Development: Various tools and APIs reduce complexity.
  • Continuous Innovation: Regular updates keep pace with emerging technologies.

From version 6.0.6 onwards, Spring requires Java 17+ and supports Groovy and Kotlin as alternative JVM languages.

Spring IoC Container

Components in Spring

In conventional three-tier architecture, applications consist of various components that Spring can manage completely. Spring handles:

  • Component instantiation
  • Property assignment
  • Inter-component references
  • Component lifecycle management

Developers only need to write metadata (configuration files) specifying which classes to manage and their relationships.

Spring IoC Container Interfaces and Implementations

The core interfaces are:

  • BeanFactory: Provides advanced configuration for managing any object type.
  • ApplicationContext: Extends BeanFactory with additional enterprise features like AOP integration, message resource handling, and application-specific implementations.

Common ApplicationContext implementations:

Implementation Description
ClassPathXmlApplicationContext Creates IoC container by reading XML configuration files from the classpath.
FileSystemXmlApplicationContext Creates IoC container by reading XML configuration files from the filesystem.
AnnotationConfigApplicationContext Creates IoC container by reading Java configuration classes.
WebApplicationContext Specialized for web applications, storing objects in ServletContext.

Spring Configuration Approaches

Spring offers three primary configuration methods:

  1. XML Configuration: The original approach defining beans and dependencies in XML files.
  2. Annotation Configuration: Introduced in Spring 2.5, using annotations like @Component to define beans.
  3. Java Configuration Class: Introduced in Spring 3.0, using @Configuration and @Bean annotations.

Inversion of Control (IoC) and Dependency Injection (DI)

IoC transfers object creation and management control from the application to the IoC container. When an application needs an object, it no longer creates it directly but relies on the container.

DI handles dependency relationships within the container, eliminating hard-coded dependencies between objects. Spring supports three DI forms:

  • Constructor injection
  • Setter method injection
  • Interface injection

Spring IoC Implementation Examples

XML Configuration Approach


<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.6</version>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.10.2</version>
</dependency>

<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    
    <bean id="user1" class="com.example.entity.User"/>
    <bean id="address1" class="com.example.entity.Address"/>
    
    <!-- Static factory method -->
    <bean id="addressStatic1" class="com.example.entity.AddressStatic" 
          factory-method="getInstance"/>
    
    <!-- Instance factory method -->
    <bean id="factory" class="com.example.entity.Factory"/>
    <bean id="addressStatic2" factory-bean="factory" factory-method="getAddress"/>
    
    <!-- Constructor injection -->
    <bean id="user2" class="com.example.entity.User">
        <constructor-arg name="name" value="John"/>
        <constructor-arg name="age" value="25"/>
        <constructor-arg name="address" ref="address1"/>
    </bean>
    
    <!-- Setter injection -->
    <bean id="user3" class="com.example.entity.User">
        <property name="name" value="Jane"/>
        <property name="address" ref="address1"/>
        <property name="age" value="30"/>
    </bean>
    
</beans>

Annotation Configuration Approach

Spring provides several annotations to define beans:

Annotation Description
@Component General-purpose stereotype annotation for any Spring-managed component.
@Repository Stereotype for persistence layer components.
@Service Stereotype for service layer components.
@Controller Stereotype for Spring MVC controllers.
// Configuration to enable component scanning
<context:component-scan base-package="com.example.service"/>

// Component example
@Component("dataService")
public class DataService {
    // Business logic implementation
}

// Autowiring example
@Controller("userController")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    public void processRequest() {
        userService.processData();
    }
}

Java Configuration Class Approach

@Configuration
@ComponentScan(basePackages = {"com.example.components"})
@PropertySource("classpath:application.properties")
public class AppConfig {
    
    @Bean
    public DataSource createDataSource(
            @Value("${db.username}") String username,
            @Value("${db.password}") String password,
            @Value("${db.url}") String url,
            @Value("${db.driver}") String driverClassName) {
        
        DataSource dataSource = new DataSource();
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        dataSource.setDriverClassName(driverClassName);
        return dataSource;
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
}

Bean Scopes and Lifecycle Methods

Bean scopes define the lifecycle and visibility of bean instances:

Scope Description Creation Timing Default
singleton Single instance in the IoC container During IoC container initialization Yes
prototype Multiple instances in the IoC container When bean is requested No
request Instance per HTTP request (web apps) Per request No
session Instance per HTTP session (web apps) Per session No

Lifecycle methods can be specified using:

  • XML: init-method and destroy-method attributes
  • Annotations: @PostConstruct and @PreDestroy

Spring AOP (Aspect-Oriented Programming)

AOP Concepts

AOP complements Object-Oriented Programming by addressing cross-cutting concerns that span multiple objects. It separates core business logic from auxiliary functionalities like logging, transaction management, and security.

Key AOP terminology:

  • Cross-cutting Concerns: functionalities that affect multiple parts of the application (e.g., logging, security)
  • Advice: action taken by an aspect at a particular join point (before, after, around)
  • Join Point: point during the execution of a program (e.g., method execution)
  • Pointcut: predicate that matches join points
  • Aspect: module that encapsulates cross-cutting concerns
  • Target: object being advised by one or more aspects
  • Proxy: object created after applying advice to a target object
  • Weaving: linking aspects with other application types or objects to create an advised object

AOP Implementation with Annotations

Dependencies

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>6.0.6</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.6</version>
</dependency>

Service Interface and Implementation

public interface MathService {
    int add(int a, int b);
    int subtract(int a, int b);
    int multiply(int a, int b);
    int divide(int a, int b);
}

@Component
public class MathServiceImpl implements MathService {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) {
        return a - b;
    }

    @Override
    public int multiply(int a, int b) {
        return a * b;
    }

    @Override
    public int divide(int a, int b) {
        return a / b;
    }
}

Aspect Implementation

@Aspect
@Component
public class LoggingAspect {
    
    @Before("execution(* com.example.service.MathServiceImpl.*(..))")
    public void logBeforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("[Before] Method " + methodName + " called with args: " + Arrays.toString(args));
    }
    
    @AfterReturning(pointcut = "execution(* com.example.service.MathServiceImpl.*(..))", 
                    returning = "result")
    public void logAfterMethod(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[AfterReturning] Method " + methodName + " returned: " + result);
    }
    
    @AfterThrowing(pointcut = "execution(* com.example.service.MathServiceImpl.*(..))", 
                   throwing = "exception")
    public void logAfterException(JoinPoint joinPoint, Throwable exception) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[AfterThrowing] Method " + methodName + " threw exception: " + exception);
    }
    
    @After("execution(* com.example.service.MathServiceImpl.*(..))")
    public void logAfterFinally(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[After] Method " + methodName + " execution completed");
    }
    
    @Around("execution(* com.example.service.MathServiceImpl.*(..))")
    public Object logAroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        System.out.println("[Around] Before " + methodName + " with args: " + Arrays.toString(args));
        
        try {
            Object result = joinPoint.proceed();
            System.out.println("[Around] After " + methodName + " with result: " + result);
            return result;
        } catch (Exception e) {
            System.out.println("[Around] Exception in " + methodName + ": " + e);
            throw e;
        }
    }
}

Configuration to Enable AOP

@Configuration
@ComponentScan(basePackages = "com.example")
@EnableAspectJAutoProxy
public class AppConfig {
    // Configuration code
}

Pointcut Expressions

Pointcut expressions define which methods should be intercepted:

  • execution(* com.example.service.*.*(..)): All methods in the service package
  • execution(public * com.example.service..*(..)): All public methods in service package and subpackages
  • execution(* com.example.service.*.*(String,..)): Methods with first parameter of type String

Aspect Ordering

When multiple aspects apply to the same method, use @Order to control execution order:

@Aspect
@Component
@Order(1)
public class FirstAspect {
    // Aspect implementation
}

@Aspect
@Component
@Order(2)
public class SecondAspect {
    // Aspect implementation
}

Spring Transaction Management

Transaction Management Approaches

Spring offers two transaction management approaches:

  1. Programmatic Transaction Management: Explicit transaction control through code
  2. Declarative Transaction Management: Transaction control through configuration or annotations

Declarative Transaction Management

Dependencies

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>6.0.6</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>6.0.6</version>
</dependency>

Configuration

@Configuration
@ComponentScan(basePackages = "com.example.tx")
@EnableTransactionManagement
@PropertySource("classpath:jdbc.properties")
public class TransactionConfig {
    
    @Value("${jdbc.driver}")
    private String driver;
    
    @Value("${jdbc.url}")
    private String url;
    
    @Value("${jdbc.username}")
    private String username;
    
    @Value("${jdbc.password}")
    private String password;
    
    @Bean
    public DataSource dataSource() {
        DataSource dataSource = new DataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
    
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

Service with Transactional Methods

@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private InventoryService inventoryService;
    
    @Transactional
    public void placeOrder(Order order) {
        // Create order
        orderRepository.save(order);
        
        // Update inventory
        inventoryService.reduceInventory(order.getItems());
    }
    
    @Transactional(readOnly = true)
    public Order getOrder(Long orderId) {
        return orderRepository.findById(orderId);
    }
    
    @Transactional(timeout = 30)
    public void processBatchOrders(List<Order> orders) {
        for (Order order : orders) {
            processOrder(order);
        }
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void riskyOperation() throws Exception {
        // Code that might throw exceptions
    }
    
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void readCommittedOperation() {
        // Operation with read committed isolation
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void newTransactionOperation() {
        // Operation that always creates a new transaction
    }
}

Transaction Attributes

The @Transactional annotation supports several atributes:

  • readOnly: Sets the transaction as read-only
  • timeout: Transacsion timeout in seconds
  • rollbackFor: Exceptions that trigger rollback
  • noRollbackFor: Exceptions that don't trigger rollback
  • isolation: Transaction isolation level
  • propagation: Transaction propagation behavior

Transaction Isolation Levels

Level Description
DEFAULT Use default isolation level of the underlying database
READ_UNCOMMITTED Allows reading uncommitted data
READ_COMMITTED Only allows reading committed data
REPEATABLE_READ Ensures repeated reads return the same data
SERIALIZABLE Highest isolation level, prevents all concurrency issues

Transaction Propagation Behaviors

Behavior Description
REQUIRED Supports current transaction; creates new if none exists
REQUIRES_NEW Creates new transaction, suspends current if exists
SUPPORTS Supports current transaction; executes non-transactionally if none
NOT_SUPPORTED Executes non-transactionally; suspends current if exists
MANDATORY Must execute within existing transaction; throws exception if none
NEVER Must execute non-transactionally; throws exception if transaction exists
NESTED Executes within nested transaction if exists

Tags: spring-framework inversion-of-control dependency-injection aspect-oriented-programming transaction-management

Posted on Sun, 17 May 2026 21:54:11 +0000 by FuriousIrishman