Skip to content

manfredma/spring-simple

Repository files navigation

spring-simple

A minimal Spring Framework implementation for learning purposes, built with Java 8 and Maven.

Overview

spring-simple reproduces the core internals of Spring Framework from scratch, including IoC container, Bean lifecycle, circular dependency resolution (three-level cache), AOP dynamic proxy, and declarative transaction management. The module structure mirrors the official Spring project.

55 tests, all passing.

Module Structure

spring-simple/
├── spring-core/        # Utilities: Resource loading, ReflectionUtils, ClassUtils, StringUtils, Assert
├── spring-beans/       # IoC core: BeanFactory hierarchy, BeanDefinition, three-level cache, lifecycle
├── spring-context/     # ApplicationContext, @ComponentScan, annotation-driven, event mechanism
├── spring-aop/         # JDK dynamic proxy, CGLIB proxy, AspectJ expression pointcut, interceptor chain
├── spring-tx/          # @Transactional, DataSourceTransactionManager, transaction propagation
└── spring-test/        # Integration tests

Dependency Graph

spring-core
    └── spring-beans
            └── spring-context
            └── spring-aop
                    └── spring-tx

Core Features

1. IoC Container

Full BeanFactory hierarchy — DefaultListableBeanFactory, AbstractAutowireCapableBeanFactory, AbstractBeanFactory — with annotation-driven configuration via AnnotationConfigApplicationContext.

@Configuration
@ComponentScan("com.example")
public class AppConfig {}

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
UserService svc = ctx.getBean(UserService.class);

Supported annotations: @Component, @Service, @Repository, @Controller, @Configuration, @Bean, @Autowired, @Qualifier, @Value, @Scope, @Lazy.

2. Bean Lifecycle

Full lifecycle pipeline:

Instantiation
    → populateBean (@Autowired / PropertyValues)
    → Aware callbacks (BeanNameAware, BeanFactoryAware, ApplicationContextAware)
    → BeanPostProcessor#postProcessBeforeInitialization
    → InitializingBean#afterPropertiesSet / init-method
    → BeanPostProcessor#postProcessAfterInitialization  ← AOP proxy created here
    → [Bean ready in singletonObjects]
    → DisposableBean#destroy / destroy-method  ← on container close

3. Circular Dependency — Three-Level Cache

Setter/field injection circular dependencies are resolved transparently.

Cache Type Purpose
singletonObjects Map<String, Object> Fully initialized singletons
earlySingletonObjects Map<String, Object> Early-exposed references (may be AOP proxy)
singletonFactories Map<String, ObjectFactory<?>> Factory lambdas — allows proxy creation on first early access

The third level exists specifically for AOP: when bean A is circularly referenced, ObjectFactory.getObject() is invoked to potentially wrap A in a proxy before the full initialization completes, ensuring all references point to the same proxy instance.

// A depends on B, B depends on A — works out of the box
@Service
public class ServiceA {
    @Autowired private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired private ServiceA serviceA;
}

4. AOP Dynamic Proxy

ProxyFactory automatically picks the proxy strategy:

Condition Strategy
Target class implements interface(s) JDK dynamic proxy (java.lang.reflect.Proxy)
Target class has no interface / proxyTargetClass=true CGLIB bytecode proxy

Advice types supported: @Before, @After, @AfterReturning, @AfterThrowing, @Around.

@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}

    @Before("serviceLayer()")
    public void before(JoinPoint jp) {
        System.out.println("Before: " + jp.getSignature());
    }

    @Around("serviceLayer()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        System.out.println("Elapsed: " + (System.currentTimeMillis() - start) + "ms");
        return result;
    }
}

5. Declarative Transaction Management

@Transactional is implemented on top of AOP — TransactionInterceptor wraps each annotated method with begin / commit / rollback logic. TransactionSynchronizationManager binds the JDBC Connection to the current thread via ThreadLocal.

@Service
public class OrderService {

    @Transactional
    public void placeOrder(Order order) {
        orderDao.insert(order);
        inventoryDao.deduct(order.getSkuId(), order.getQty());
        // Any RuntimeException triggers automatic rollback
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void auditLog(String msg) {
        // Runs in an independent transaction regardless of the caller's state
        auditDao.insert(msg);
    }
}

Supported propagation behaviors: REQUIRED, REQUIRES_NEW, SUPPORTS, NOT_SUPPORTED, MANDATORY, NEVER, NESTED.

Build & Test

Requirements: JDK 8+, Maven 3.6+

# Build all modules (skip tests)
mvn clean package -DskipTests -Dsort.skip=true

# Run all tests
mvn clean test -Dsort.skip=true

# Run a specific module's tests
mvn clean test -pl spring-test -Dsort.skip=true

# Run a specific test class
mvn clean test -pl spring-test -Dtest=CircularDependencyTest -Dsort.skip=true

Test Coverage

Test Class Module What It Covers
IocContainerTest spring-test Bean registration, @Autowired, @Value, prototype scope
ApplicationContextTest spring-test AnnotationConfigApplicationContext, lifecycle callbacks, events
CircularDependencyTest spring-test A→B→A circular dependency with and without AOP
JdkProxyTest spring-test JDK dynamic proxy, @Before / @After / @Around advice
CglibProxyTest spring-test CGLIB proxy for concrete classes
TransactionTest spring-test Commit, rollback, REQUIRED, REQUIRES_NEW propagation

Project Layout (Key Source Files)

spring-beans/
  └── factory/support/
        ├── DefaultSingletonBeanRegistry.java   # Three-level cache
        ├── AbstractAutowireCapableBeanFactory.java  # createBean / populateBean / initializeBean
        └── DefaultListableBeanFactory.java     # Full BeanFactory implementation

spring-context/
  └── support/
        ├── AbstractApplicationContext.java     # refresh() 12-step lifecycle
        ├── AnnotationConfigApplicationContext.java
        └── ClassPathBeanDefinitionScanner.java  # @ComponentScan implementation

spring-aop/
  └── framework/
        ├── ProxyFactory.java                   # JDK vs CGLIB selection
        ├── JdkDynamicAopProxy.java
        ├── CglibAopProxy.java
        └── ReflectiveMethodInvocation.java     # Interceptor chain execution

spring-tx/
  └── interceptor/
        └── TransactionInterceptor.java         # @Transactional AOP interceptor
  └── datasource/
        └── DataSourceTransactionManager.java   # JDBC transaction manager
  └── support/
        └── TransactionSynchronizationManager.java  # ThreadLocal connection binding

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages