Overview
Spring is less a framework than an ecosystem: an IoC container (spring-core,
spring-context) with everything else — web, data, security, messaging, batch — layered
on top and wired by Spring Boot's auto-configuration. Its power is the integration
surface: whatever you need to talk to, there is a starter for it. Its costs are runtime
reflection, proxy-based AOP with real gotchas, and a footprint the build-time frameworks
were invented to undercut — which Boot 3.x answers with GraalVM AOT and virtual-thread
support.
Key points
- IoC container: beans defined via
@Configuration+@Beanmethods or component scanning (@Component,@Service,@Repository); singleton scope by default; lifecycle callbacks (@PostConstruct,@PreDestroy,BeanPostProcessor). - AOP by proxy:
@Transactional,@Cacheable,@Asyncwork through JDK dynamic proxies or CGLIB subclasses — hence the self-invocation gotcha (calls within the same bean bypass the proxy and the advice silently never runs). - Spring Boot: opinionated auto-configuration driven by classpath presence and
@ConditionalOn*conditions; starters (spring-boot-starter-web,-data-jpa,-security); Actuator for health/metrics; externalized config with profiles and relaxed binding. - Two web stacks: Spring MVC (servlet, blocking,
spring-webmvcon Tomcat/Jetty) vs WebFlux (reactive,spring-webfluxon Reactor/Netty) — don't mix casually. - Spring Data: repository interfaces with derived queries; Spring Security: an
ordered servlet filter chain; declarative transactions via
@Transactional. - Boot 3.x baseline: Java 17+,
jakarta.*namespace (Jakarta EE 10), AOT engine for GraalVM native image,spring.threads.virtual.enabled=truefor Loom. - Standards stance: Spring implements few Jakarta specs itself but consumes them — JPA via Hibernate, the Servlet API, Bean Validation via Hibernate Validator.
Details
Container and configuration
The ApplicationContext builds a bean graph at startup: component scanning finds
annotated classes, @Configuration classes contribute @Bean factory methods, and
dependency injection is by constructor (preferred), setter, or field. Startup cost is
proportional to scanning and reflection — the exact work Quarkus/Micronaut shift to build
time. @Profile and @Conditional gate beans per environment.
AOP and its gotchas
Spring AOP is proxy-based, not bytecode weaving (that would be AspectJ). Consequences
worth internalizing: only public external calls are advised; self-invocation (this. transactionalMethod()) skips the proxy — the classic "why didn't my transaction roll
back" bug; CGLIB proxies require a non-final class and no-arg constructible hierarchy.
Transactions
@Transactional semantics: propagation (REQUIRED default, REQUIRES_NEW suspends),
isolation, readOnly as a hint, and rollback rules — unchecked exceptions roll back by
default, checked ones do not unless rollbackFor says so. Under the hood it is an
interceptor binding a JDBC/JPA transaction to the thread — which is exactly why it
interacts carefully with virtual threads and not at all with code that hops threads.
Web, data, security
- MVC:
@RestController,@RequestMapping, message conversion via Jackson;RestClient(sync) andWebClient(reactive) as HTTP clients. - WebFlux:
Mono/Fluxfrom Project Reactor end-to-end; worth it only when the whole call chain (including the driver — R2DBC, reactive Mongo) is non-blocking. Virtual threads erode much of its motivation for plain request/response services. - Spring Data:
JpaRepository<User, Long>with derived queries (findByEmailAndActiveTrue),@Queryfor the rest; sibling modules for Mongo, Redis, Elasticsearch keep the same programming model. - Spring Security: a
SecurityFilterChainbean of ordered filters (authentication → authorization); first-class OAuth2/OIDC client and resource-server support (spring-boot-starter-oauth2-resource-server).
Testing
Test slices load partial contexts: @WebMvcTest (controllers + MockMvc),
@DataJpaTest (repositories + embedded/Testcontainers DB), @JsonTest. Full-context
@SpringBootTest is the integration hammer — slow, so cache-friendly test design
(consistent context configuration) matters. @MockitoBean (formerly @MockBean)
replaces beans with mocks; @ServiceConnection wires Testcontainers straight into config.
Examples
@Service
public class OrderService {
private final OrderRepository repo;
OrderService(OrderRepository repo) { this.repo = repo; } // constructor injection
@Transactional
public Order place(OrderRequest req) { ... }
public void batch(List<OrderRequest> reqs) {
reqs.forEach(this::place); // BUG: self-invocation — @Transactional not applied
}
}
Related
- Java application frameworks — the landscape — parent overview and comparison table.
- Frameworks × Jakarta EE — who implements what — where Spring's own APIs map onto the specs it shadows.
- GraalVM — the native-image toolchain behind Boot 3 AOT.
- Integration testing —
@SpringBootTest, slices and Testcontainers in practice. - OAuth2 and OIDC — what Spring Security's OAuth2 support implements.