Spring là gì — bài toán framework giải quyết và bản đồ kiến trúc
Vì sao Spring xuất hiện năm 2003 và vẫn dominate sau 22 năm. Bài toán Java EE để lại, Rod Johnson và quyển sách 800 trang, kiến trúc 20 module của Spring Framework, 5 lớp cốt lõi của Spring Boot, vòng đời 1 HTTP request, và lộ trình các bài tiếp theo bóc tách từng phần.
TL;DR: Spring ra đời 2003-2004 vì Java EE/EJB 2.x quá phức tạp (5 file cho 1 method nghiệp vụ, vendor lock-in, không test offline được). Idea cốt lõi: object lắp ráp lẫn nhau qua new gây tight coupling — IoC container đảo ngược trách nhiệm này. Spring Framework là bộ ~20 module xây trên IoC; Spring Boot không phải framework mới mà là 5 thứ đóng gói: Framework + Starter + AutoConfig + Embedded server + Actuator. Pitfall lớn nhất: nhầm Boot là Spring "phiên bản mới" và bỏ qua Framework — khi debug bean injection hay transaction rollback, bạn đang chạm vào Framework.
Bạn mở một dự án Java backend bất kỳ ở Việt Nam — banking, e-commerce, fintech, gov-tech — gần như chắc chắn thấy import org.springframework.* ở đầu mọi file. Spring xuất hiện ở 80% job description Java; nhiều nơi viết thẳng "Spring Boot" thay vì "Java" trong tên vị trí. Câu hỏi tự nhiên là: vì sao một framework ra mắt 2003, lúc bạn còn chưa sinh ra hoặc đang học mẫu giáo, lại vẫn là default choice năm 2026?
Bài này dài vì cover bối cảnh + kiến trúc + lịch sử. Bạn sẽ đi qua: (1) bài toán Java EE để lại, (2) ý tưởng cốt lõi Spring giải quyết, (3) bản đồ kiến trúc 20 module của Spring Framework, (4) 5 lớp cốt lõi của Spring Boot, (5) vòng đời 1 HTTP request từ socket đến database. Sau bài này, mọi annotation, magic, autoconfig của các bài sau hiện ra như logic, không còn là phép thuật.
1. Bối cảnh — Java EE 2001 và lý do nó thất bại
Đầu thập niên 2000, Java enterprise đứng trên 3 chân: EJB 2.x (Enterprise JavaBeans), JNDI, và application server kiểu WebLogic, WebSphere, JBoss. Bạn muốn viết 1 service đơn giản — ví dụ BookingService lưu booking vào DB — bạn phải viết:
- 1 interface remote (
BookingServiceRemote) extendEJBObject. - 1 interface home (
BookingServiceHome) extendEJBHomevới methodcreate(). - 1 bean class (
BookingServiceBean) implementSessionBeanvớiejbCreate(),ejbActivate(),ejbPassivate(),ejbRemove(),setSessionContext(). - 1 file XML deployment descriptor
ejb-jar.xml~80 dòng khai báo bean. - 1 file XML vendor-specific
weblogic-ejb-jar.xmlđể map JNDI name.
Tổng cộng: 5 file cho 1 method nghiệp vụ. Test? Không thể test ngoài app server. Chạy local? Phải start cả WebLogic 4GB RAM. Đổi từ WebLogic sang JBoss? Viết lại vendor descriptor.
flowchart LR
Client["Client"] --> Home["BookingServiceHome<br/>(JNDI lookup)"]
Home --> Remote["BookingServiceRemote"]
Remote --> Bean["BookingServiceBean<br/>(impl thuc te)"]
Bean --> DB[("Database")]
style Home fill:#fee
style Remote fill:#feeBốn vấn đề thực tế gặp khi làm EJB 2.x:
| Vấn đề | Hệ quả với team |
|---|---|
| Vendor lock-in | Đổi WebLogic → JBoss tốn 2-3 tháng do descriptor khác và bug subtle |
| Test offline không được | Mỗi lần chạy unit test phải boot WebLogic — mỗi cycle 1-3 phút |
| Boilerplate khổng lồ | 5 file/service, 70% code là plumbing không phải nghiệp vụ |
| Lifecycle phức tạp | ejbActivate, ejbPassivate, ejbStore, ejbLoad — khó debug khi gọi sai thứ tự |
Rod Johnson — kỹ sư người Úc làm tư vấn enterprise Java — viết quyển "Expert One-on-One J2EE Design and Development" năm 2002, dày 700 trang. Quyển sách dành chương cuối kèm theo 30,000 dòng code mẫu để demo: cùng nghiệp vụ ấy, viết bằng plain Java POJO (Plain Old Java Object) với một container nhẹ để inject dependency, ngắn hơn 10 lần và test được offline. Code đó tên là Interface21. Năm 2003, Johnson cùng Juergen Hoeller open-source nó dưới tên Spring Framework — phiên bản 1.0 ra ngày 24/03/2004.
Tên "Spring" được chọn theo nghĩa "spring after a long winter of J2EE" — mùa xuân sau mùa đông dài của J2EE. Cái tên là tuyên ngôn ngầm: framework này thoát khỏi sự cồng kềnh của EJB.
Quyển sách gốc của Rod Johnson vẫn truy cập được trên Wiley và một số mirror — phần Appendix giới thiệu kiến trúc Interface21 chính là phôi thai của Spring. Đọc Chapter 1-2 đủ thấy bối cảnh: J2EE phức tạp, vendor lock-in, test khó.
Tham khảo timeline chính thức: spring.io/blog/2007/02/27/spring-the-history (bản chính chủ).
2. Bài toán cốt lõi — không phải về annotation, mà về quyền điều khiển
Quên Spring đi 1 phút. Nghĩ về 1 đoạn code Java thuần, chưa có framework nào:
public class OrderService {
private PaymentGateway payment = new StripePayment();
private EmailService email = new SmtpEmailService();
private InventoryClient inventory = new HttpInventoryClient("http://inv:8080");
public void placeOrder(Order order) {
inventory.reserve(order.items());
payment.charge(order.total());
email.send(order.customer(), "Order confirmed");
}
}
Code này chạy được. Nhưng nó vướng 5 vấn đề mà mọi backend developer phải đối mặt khi codebase lớn lên:
| Vấn đề | Triệu chứng |
|---|---|
| Tight coupling | OrderService biết chính xác StripePayment, SmtpEmailService — không thể đổi sang MoMoPayment mà không sửa OrderService |
| Khó test | Test placeOrder() sẽ thật sự gọi Stripe API và SMTP server — chậm, flaky, tốn tiền |
| Cấu hình rò rỉ | URL http://inv:8080 hardcode — đổi staging vs prod phải build lại |
| Lifecycle khó kiểm soát | Ai close connection của SmtpEmailService khi app shutdown? Ai retry khi Stripe down? |
| Object soup | App lớn có 200 service — ai chịu trách nhiệm new đúng thứ tự? Service A cần B, B cần C, C cần A vòng tròn — sẽ phát hiện khi nào? |
Ba bài học từ 5 vấn đề trên:
- Object cần được "lắp ráp" bởi một thực thể bên ngoài, không phải tự lắp ráp lẫn nhau.
- Object cần được mô tả qua interface, để có thể thay implementation.
- Lifecycle (tạo, init, destroy) cần một entity quản lý, không phải code nghiệp vụ.
Đó chính là 3 chức năng của IoC container — phần lõi của Spring. Phần còn lại của framework (web, data, security, ...) đều xây trên hạt nhân này.
3. Bản đồ kiến trúc — Spring Framework có gì
Spring Framework không phải 1 jar khổng lồ. Nó là bộ ~20 module, mỗi module giải quyết 1 nhóm vấn đề, có jar riêng, có thể dùng độc lập. Sơ đồ chính thức từ docs Spring (đơn giản hoá):
flowchart TB
subgraph Core["Core Container (BAT BUOC)"]
spc["spring-core<br/>(util, classloader, IO)"]
spb["spring-beans<br/>(BeanFactory, DI engine)"]
spx["spring-context<br/>(ApplicationContext, events)"]
spe["spring-expression<br/>(SpEL)"]
end
subgraph DataAccess["Data Access / Integration"]
spj["spring-jdbc"]
spo["spring-orm<br/>(JPA, Hibernate)"]
spt["spring-tx<br/>(@Transactional)"]
spjms["spring-jms"]
spoxm["spring-oxm<br/>(XML marshalling)"]
end
subgraph Web["Web Layer"]
spw["spring-web<br/>(HTTP foundations)"]
spmvc["spring-webmvc<br/>(@Controller, DispatcherServlet)"]
spwf["spring-webflux<br/>(reactive)"]
end
subgraph Aspects["AOP & Instrumentation"]
spa["spring-aop"]
spaj["spring-aspects<br/>(AspectJ integration)"]
end
subgraph Test["Testing"]
spt2["spring-test<br/>(MockMvc, slice tests)"]
end
Core --> DataAccess
Core --> Web
Core --> Aspects
Core --> TestBảng module quan trọng cần biết:
| Module | Vai trò | Nơi đào sâu trong track |
|---|---|---|
spring-core | Util, classloader, resource abstraction | Course này — Container Internals |
spring-beans | BeanFactory, dependency injection engine | Course này — IoC & DI, Container Internals |
spring-context | ApplicationContext, event publisher, i18n | Course này — Container Internals |
spring-expression | SpEL — expression language cho @Value, @PreAuthorize | Course này (Config) + Spring Security |
spring-aop | Aspect-Oriented Programming, proxy mechanism | Course này — Lifecycle & Scopes (AOP proxy) |
spring-tx | @Transactional, transaction abstraction | Spring REST & Data |
spring-jdbc | JdbcTemplate, JdbcClient | Spring REST & Data |
spring-orm | JPA/Hibernate integration | Spring REST & Data |
spring-web | HTTP foundations, multipart, codec | Spring REST & Data |
spring-webmvc | Spring MVC servlet stack | Spring REST & Data |
spring-webflux | Reactive web stack | Spring Reactive & Microservices |
spring-test | MockMvc, @SpringBootTest, slice testing | Spring Security & Testing |
Hệ quả thực tế: khi bạn thấy import org.springframework.beans.factory.annotation.Autowired, bạn biết @Autowired ở module spring-beans. Khi thấy import org.springframework.transaction.annotation.Transactional, bạn biết module spring-tx. Hiểu mapping này giúp đọc stack trace nhanh hơn — package org.springframework.X thường tương ứng module spring-X.
3.1 IoC container — module spring-beans + spring-context
Đây là trái tim của Spring. Khi bạn nghe "Spring là IoC container", chính xác là 2 module này. Chúng cung cấp:
BeanFactory— interface gốc tối giản (sẽ bóc ở bài 03).ApplicationContext— interface đầy đủ extend BeanFactory với i18n, event, environment.- Annotation
@Component,@Service,@Repository,@Controller,@Configuration,@Bean,@Autowired,@Qualifier,@Value,@Scope,@PostConstruct,@PreDestroy. - Component scanner — quét classpath tìm bean.
- Dependency resolution engine — topological sort, ambiguity handling.
Mọi module Spring khác đều build trên IoC container. spring-webmvc đăng ký DispatcherServlet như 1 bean; spring-tx đăng ký TransactionManager như 1 bean; spring-data đăng ký repository như bean. Không có module nào tự sống độc lập với container.
3.2 Web stack — 2 lựa chọn không cùng tồn tại
Spring có 2 web stack hoàn toàn khác kiến trúc:
| Aspect | Spring MVC (spring-webmvc) | Spring WebFlux (spring-webflux) |
|---|---|---|
| Model | Servlet, blocking I/O | Reactive Streams, non-blocking |
| Server | Tomcat, Jetty, Undertow (servlet) | Netty, hoặc servlet 3.1+ async |
| Programming | Imperative (return User) | Reactive (return Mono<User>) |
| Khi nào chọn | Default 95% case | I/O-bound cao, streaming, server push |
WebFlux không thay MVC — chúng song song tồn tại, chọn theo nhu cầu. Track này dạy MVC ở course Spring REST & Data (chính), WebFlux ở course Spring Reactive & Microservices (advanced).
3.3 Data access — JDBC, ORM, Transaction
Spring có 3 layer data access từ thấp đến cao:
flowchart TB
A["Business code"]
B["spring-data-jpa<br/>Repository<User, Long>"]
C["spring-orm<br/>JPA / Hibernate / EntityManager"]
D["spring-jdbc<br/>JdbcTemplate / JdbcClient"]
E["JDBC driver / DataSource"]
A --> B
A --> C
A --> D
B --> C
C --> D
D --> E
style B fill:#d1fae5spring-jdbc: low-level —JdbcTemplateviết SQL trực tiếp, parameter binding tự động, exception translation. Phù hợp khi bạn cần kiểm soát SQL đầy đủ.spring-orm: integrate JPA/Hibernate vào IoC container —EntityManagerthành bean inject được.spring-data-jpa(separate project, không thuộc Framework core): thêm Repository abstraction — bạn khai báo interface, Spring sinh implementation. Ngày nay là default cho 90% app.
spring-tx là cross-cutting — @Transactional hoạt động trên cả 3 layer trên.
4. Spring Boot — không phải framework mới, là 5 thứ đóng gói
Đến 2014, Spring 4 vẫn yêu cầu mỗi project mới làm 5 thao tác:
- Khai báo dependency
spring-context,spring-web,spring-orm, ... vàopom.xml. - Tạo
web.xmlkhai báoDispatcherServlet. - Tạo
applicationContext.xml(hoặc Java config class) khai báo bean. - Cấu hình embedded server (Jetty/Tomcat) hoặc deploy WAR vào server ngoài.
- Cấu hình logging (Logback), DataSource, JPA properties, ...
Ai cũng làm 5 bước y hệt. Spring Boot ra đời 2014 với 1 idea đơn giản: gom 5 bước đó thành opinionated defaults — bạn chỉ cần khai báo "tôi muốn build web app", Boot tự setup tất cả.
// Spring 4 — toi thieu 80 dong cau hinh + 4 file XML/Java
// Spring Boot 3 — 1 file, 12 dong:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
@RestController
class HelloController {
@GetMapping("/")
String hello() { return "Hello from Spring Boot"; }
}
Chạy mvn spring-boot:run — Boot tự khởi Tomcat embedded port 8080, scan classpath thấy HelloController đăng ký route, log start banner. Không có XML, không có deploy descriptor, không có server cài riêng.
Spring Boot không phải framework mới. Nó là tập hợp 5 thứ đóng gói:
flowchart LR
SB["Spring Boot"]
Core["Spring Framework<br/>(core, web, data, ...)"]
Starter["Starter Dependencies<br/>(spring-boot-starter-*)"]
Auto["Auto-Configuration<br/>(@EnableAutoConfiguration)"]
Embed["Embedded Server<br/>(Tomcat/Jetty/Netty)"]
Act["Actuator<br/>(metrics, health, info)"]
SB --> Core
SB --> Starter
SB --> Auto
SB --> Embed
SB --> Act
style SB fill:#fef3c74.1 Starter dependencies
Một starter là 1 jar rỗng — không chứa code, chỉ là collection of <dependency> trong pom.xml. Ví dụ:
<!-- spring-boot-starter-web pull theo: -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-json</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId></dependency>
Bạn chỉ khai 1 dòng spring-boot-starter-web, Maven pull tất cả transitive. Starter phổ biến:
| Starter | Pull theo |
|---|---|
spring-boot-starter-web | Spring MVC + Tomcat embedded + Jackson JSON + Validation |
spring-boot-starter-webflux | Spring WebFlux + Netty + Jackson |
spring-boot-starter-data-jpa | Spring Data JPA + Hibernate + JDBC |
spring-boot-starter-security | Spring Security |
spring-boot-starter-test | JUnit 5 + Mockito + AssertJ + Spring Test |
spring-boot-starter-actuator | Actuator endpoints |
spring-boot-starter-thymeleaf | Thymeleaf template engine |
spring-boot-starter-validation | Hibernate Validator + Jakarta Validation |
Triết lý: 1 use case = 1 starter, không phải pick từng jar.
4.2 Auto-configuration
Đây là magic chính của Boot. Khi container start, Spring Boot scan classpath, tuỳ vào jar có sẵn mà tự đăng ký bean:
| Classpath có | Boot tự register |
|---|---|
spring-webmvc | DispatcherServlet bean, HandlerMapping, MessageConverter |
tomcat-embed-core | TomcatServletWebServerFactory — embedded Tomcat |
HikariCP + DataSource config | HikariDataSource bean |
hibernate-core + DataSource | EntityManagerFactory + JpaTransactionManager |
spring-security | SecurityFilterChain default (deny all) |
micrometer-core | MeterRegistry bean |
Auto-config dựa trên @Conditional* annotation (đã thấy ở bài 06) — register bean chỉ khi điều kiện đúng (class tồn tại, bean chưa có, property bật, ...). Kết quả: thêm/xóa starter → app tự thay đổi behavior, không phải sửa config.
Phần Spring Boot Autoconfig (cuối course này) sẽ bóc tách auto-config ở mức bytecode. Bài này chỉ cần biết: Boot không tạo bean ngẫu nhiên — mỗi bean Boot tạo đều có condition rõ ràng.
4.3 Embedded server
Trước Boot, Java web app deploy WAR vào Tomcat/Jetty cài riêng. Sau Boot, server đóng gói trong jar:
mvn package
java -jar target/app.jar # Tomcat tu start trong jar
Lợi ích:
- 1 artifact duy nhất — dễ ship Docker, K8s.
- Process model 1 jar = 1 process — phù hợp cloud-native.
- Test local không cần cài server.
Boot support 4 embedded server:
| Server | Default cho | Đặc điểm |
|---|---|---|
| Tomcat | spring-boot-starter-web | Default, mature, blocking I/O |
| Jetty | Opt-in qua exclude Tomcat + add Jetty starter | Lighter than Tomcat, NIO support |
| Undertow | Opt-in | Async-first, low memory |
| Netty | spring-boot-starter-webflux | Reactive default, non-blocking |
4.4 Actuator
Production-ready endpoints có sẵn — health, metrics, env, loggers, info, configprops, mappings, beans, threaddump. Thêm spring-boot-starter-actuator, expose /actuator/health, /actuator/metrics, ... ngay lập tức.
curl http://localhost:8080/actuator/health
# {"status":"UP","components":{"db":{"status":"UP"},"diskSpace":{"status":"UP"}}}
Course Spring in Production (Observability) sẽ dùng Actuator + Micrometer + Prometheus + Grafana đầy đủ.
4.5 DevTools (optional)
spring-boot-devtools: hot reload class khi save, restart context, disable cache template. Chỉ kích hoạt khi chạy local — production tự skip qua dependency scope.
5. Vòng đời 1 HTTP request — bản đồ tổng
Khi bạn gửi GET /api/orders/123 đến app Spring Boot, request đi qua những lớp nào? Đây là big picture giúp định vị mọi annotation/concept của các module sau:
sequenceDiagram
participant Client
participant TC as Tomcat Connector<br/>(port 8080)
participant Filter as Servlet Filter Chain<br/>(Security, CORS, ...)
participant DS as DispatcherServlet
participant HM as HandlerMapping
participant HI as HandlerInterceptor
participant Ctrl as @RestController<br/>OrderController
participant Svc as @Service<br/>OrderService
participant Repo as @Repository<br/>OrderRepository
participant DB as PostgreSQL
Client->>TC: GET /api/orders/123
TC->>Filter: HttpServletRequest
Filter->>DS: pass (filter doesn't reject)
DS->>HM: which handler for path?
HM-->>DS: OrderController.getOrder(Long)
DS->>HI: preHandle()
DS->>Ctrl: invoke getOrder(123)
Ctrl->>Svc: orderService.findById(123)
Svc->>Repo: orderRepository.findById(123)
Repo->>DB: SELECT * FROM orders WHERE id=123
DB-->>Repo: row data
Repo-->>Svc: Optional<Order>
Svc-->>Ctrl: Order DTO
Ctrl-->>DS: ResponseEntity<OrderDto>
DS->>DS: serialize JSON via Jackson
DS->>HI: postHandle()
DS-->>Filter: HttpServletResponse
Filter-->>TC: response with body
TC-->>Client: HTTP 200 + JSONMỗi lớp do 1 module Spring quản lý:
| Lớp | Module | Tại sao quan trọng |
|---|---|---|
| Tomcat Connector | Boot autoconfig (TomcatServletWebServerFactory) | Quản lý socket, thread pool, HTTP/1.1 / HTTP/2 |
| Servlet Filter | Servlet API + Spring Security | Auth, CORS, logging — chạy trước routing |
DispatcherServlet | spring-webmvc | Front controller, routing engine |
HandlerMapping | spring-webmvc | Map URL → method (@RequestMapping) |
HandlerInterceptor | spring-webmvc | Hook trước/sau controller (vd metrics, audit) |
@RestController | spring-webmvc | Method nghiệp vụ trả body |
@Service | IoC container (spring-context) | Bean nghiệp vụ — DI, transaction |
@Repository | spring-data-jpa | Repository sinh từ interface |
| Transaction | spring-tx | Mở/commit/rollback DB tx |
| JPA / Hibernate | spring-orm | Map row → entity |
Hệ quả: khi debug "tại sao request 401", bạn biết lớp Filter (Security). "Tại sao 404" — HandlerMapping. "Tại sao tx không rollback" — spring-tx. "Tại sao N+1 query" — spring-orm/spring-data-jpa.
Track Spring bóc từng lớp qua nhiều course:
- Course này — Spring Core (đang đọc): IoC container, lifecycle, config + Spring Boot foundations (auto-config, starter, embedded server) — phần lõi cho mọi lớp service/repo.
- Spring REST & Data: Web layer (
DispatcherServlet,@RestController, validation) + Data layer (Spring Data JPA, transaction). - Spring Security & Testing: Filter layer — Spring Security, JWT — và test slice cho từng layer.
- Spring in Production: observability cho toàn bộ chain.
- Spring Reactive & Microservices, Spring Cloud & AI: các lớp nâng cao reactive, microservice, tích hợp AI.
6. Spring Framework qua 6 phiên bản
Spring không phải framework tĩnh — nó tiến hoá theo Java và theo industry. Hiểu lịch sử các version giúp bạn đọc code legacy không hoang mang:
| Version | Năm | Cột mốc |
|---|---|---|
| 1.x | 2004 | XML config, IoC container, AOP, JDBC template |
| 2.x | 2006 | XML namespace, AspectJ pointcut, JPA |
| 3.x | 2009 | Annotation-based config (@Component, @Autowired), Java 5+ |
| 4.x | 2013 | Java 8 baseline, Spring WebSocket, generics-aware DI |
| 5.x | 2017 | WebFlux reactive, Kotlin support, Java 9 module |
| 6.x | 2022 | Jakarta EE 9+ (javax.* → jakarta.*), GraalVM native, Java 17+ baseline |
Hai bước nhảy quan trọng nhất:
- Spring 3 (2009): chuyển từ XML sang annotation. Đây là lý do code Spring legacy 2009-2014 vẫn còn
applicationContext.xml, code 2015+ chủ yếu là Java config. - Spring 6 (2022): rename namespace từ
javax.*(Java EE) sangjakarta.*(Jakarta EE) do Oracle nhường thương hiệu Java EE cho Eclipse Foundation. Đây là breaking change — code Spring 5 không build được trên Spring 6 nếu không migrate import. Khoá này dùng Spring 6.x đi cùng Spring Boot 3.x.
Bảng compatibility quan trọng — dùng để pick version cho project mới:
| Spring Boot | Spring Framework | Java tối thiểu | Jakarta namespace |
|---|---|---|---|
| 2.7.x (legacy) | 5.3.x | 8 | javax.* |
| 3.0.x | 6.0.x | 17 | jakarta.* |
| 3.2.x | 6.1.x | 17 | jakarta.* |
| 3.3.x | 6.1.x | 17 | jakarta.* |
| 3.4.x (target khoá này) | 6.2.x | 17 (kn 21) | jakarta.* |
| 3.5.x | 6.2.x | 17 (kn 21) | jakarta.* |
| 4.0.x (mới) | 7.0.x | 17 | jakarta.* |
(kn 21 = "khuyến nghị 21" để có Virtual Threads).
7. Spring ecosystem — không chỉ là "Spring Boot"
Khi search "Spring", bạn sẽ thấy hàng chục project. Đây là bản đồ rút gọn — mỗi mảng là 1 module riêng, có lifecycle release riêng, đôi khi có version riêng:
| Project | Vai trò | Version 2026 | Khi nào học trong khoá |
|---|---|---|---|
| Spring Framework | Core: IoC, AOP, MVC, WebFlux, Tx, Test | 6.2.x | Module 1, 3, 10 |
| Spring Boot | Auto-config + starter + embedded | 3.4.x / 3.5.x | Module 2 |
| Spring Data | Abstraction CRUD: JPA, MongoDB, Redis, R2DBC | 2024.x | Module 4 |
| Spring Security | Authentication, OAuth2, JWT | 6.4.x | Module 5 |
| Spring Cloud | Microservices: config, gateway, discovery | 2024.0.x | Spring Reactive & Microservices / Spring Cloud & AI |
| Spring Batch | Batch processing (ETL) | 5.2.x | (advanced — không trong khoá) |
| Spring Integration | EAI patterns (channel, transformer) | 6.4.x | (advanced — không trong khoá) |
| Spring AI | LLM: ChatClient, RAG, tool calling | 1.0.x | Module 15 |
| Spring Modulith | Module boundary trong monolith | 1.4.x | Module 14 |
| Spring Authorization Server | OAuth2/OIDC server | 1.4.x | Module 5 (tham khảo) |
Khoá này tập trung Spring Framework + Spring Boot + Spring Data + Spring Security + Spring AI. Bốn module này phủ ~95% công việc của Java backend developer mid-level. Spring Cloud, Modulith chạm đến ở Tier 3 (senior). Batch, Integration là use case ngách — không có trong khoá nhưng nền tảng IoC giống nhau, dễ pick up khi cần.
8. Vì sao Spring vẫn dominate năm 2026
Quarkus, Micronaut, Helidon — 3 framework Java backend ra đời sau 2018, đều thiết kế lại "tốt hơn Spring" trên giấy: native image first-class, startup nhanh hơn, bộ nhớ ít hơn. Nhưng Spring vẫn chiếm ~70% thị phần. Vì sao?
Bốn lý do, theo thứ tự ảnh hưởng:
- Inertia khổng lồ: 22 năm tài liệu, Stack Overflow, sách. Bạn gặp bug — search ra 30 issue cũ + giải pháp. Quarkus/Micronaut có vài năm — community còn nhỏ, search ra ít kết quả.
- Ecosystem chiều rộng: tất cả integration (Kafka, Redis, Postgres, S3, OAuth provider, Vault, ...) đều có Spring starter sẵn. Quarkus đang đuổi nhưng chưa tới — vd Spring AI có sẵn provider cho 8 LLM, Quarkus LangChain4j chưa có Anthropic native.
- Spring Boot 3.2+ bắt kịp: Virtual Threads (Java 21), GraalVM native, observability với Micrometer — không còn yếu thế về performance/startup. Boot 3.3 + CDS + Java 21 = startup ~500ms cho app cỡ vừa.
- Pivotal/VMware/Broadcom đầu tư đều đặn — Spring có team trả lương full-time, không phải open source kiểu hobby. Release đều mỗi 6 tháng.
Câu hỏi đúng không phải "Spring có còn phù hợp?", mà là "khi nào dùng Spring vs Quarkus?". Câu trả lời ngắn:
- Default Spring: web app, microservices, monolith, anything với team Java mainstream.
- Quarkus: serverless function với cold-start critical (AWS Lambda, GCP Functions), native image là yêu cầu cứng (memory dưới 64MB), team đã có kinh nghiệm Quarkus từ trước.
9. 📚 Deep Dive Spring Reference
Reference docs gốc (đọc khi gặp annotation/concept lạ):
- Spring Framework Reference — Overview — paragraph đầu giới thiệu triết lý của Spring, viết ngắn nhưng đủ ý.
- Spring Framework Reference — Index — TOC đầy đủ Framework. Bookmark.
- Spring Boot Reference — Documentation Index — trang chủ docs Boot.
- Spring Boot Reference — Introducing Spring Boot — section ngắn giải thích Boot vs Framework.
Bản đồ ecosystem:
- Spring Projects — bản đồ ecosystem chính thức, click vào module thấy version compatibility.
- Spring Boot Compatibility Matrix — xem version Spring Boot nào đang được support, cho đến khi nào.
Lịch sử:
- spring.io/blog/2007/02/27/spring-the-history — Rod Johnson kể lại lịch sử Spring chính thức.
Bài viết kinh điển ngoài docs:
- Martin Fowler — Inversion of Control Containers and the Dependency Injection pattern (2004) — bài viết hệ thống hoá thuật ngữ. Đọc 1 lần để có vocabulary chuẩn (sẽ refer lại ở bài 02).
Ghi chú: đọc reference của Spring không phải đọc tuyến tính như sách giáo khoa. Mỗi khi gặp annotation lạ, search reference, đọc 1 section, hiểu, quay lại code. Đó là pattern dùng docs hiệu quả. Ưu tiên reference docs hơn random blog — Spring docs được team Spring viết và update theo từng release, độ chính xác cao nhất.
10. Tóm tắt
- Spring ra đời 2003-2004 để giải quyết sự phức tạp và vendor lock-in của Java EE/EJB 2.x.
- Bài toán cốt lõi: object lắp ráp lẫn nhau qua
newdẫn đến tight coupling, khó test, lifecycle khó kiểm soát. Lời giải: IoC container — entity bên ngoài chịu trách nhiệm tạo, lắp ráp, lifecycle. - Spring Framework là bộ ~20 module, tất cả xây trên IoC container (
spring-beans+spring-context). Modules theo nhóm: Core, Data Access, Web, AOP, Test. - Spring Boot không phải framework mới. Nó là 5 thứ đóng gói: Spring Framework + Starter dependency + Auto-configuration + Embedded server + Actuator.
- Web app có 2 stack song song: MVC (servlet, blocking) — default 95% case, WebFlux (reactive, non-blocking) — I/O-heavy/streaming.
- Vòng đời 1 HTTP request đi qua 10 lớp — mỗi lớp do module Spring khác quản. Hiểu mapping này giúp debug nhanh.
- Spring 6 (2022) là cột mốc rename
javax.*→jakarta.*. Khoá này dùng Spring Boot 3.4.x + Spring Framework 6.2.x + Java 21. - Ecosystem có hàng chục project. Khoá tập trung Framework + Boot + Data + Security + AI — phủ 95% việc của backend developer.
- Năm 2026 Spring vẫn dominate nhờ inertia, ecosystem rộng, và Boot 3.2+ đã bắt kịp về startup/native với competitor.
11. Tự kiểm tra
Q1Một developer mới học Spring nói: "Spring Boot là Spring phiên bản mới, dùng Boot là không cần học Spring nữa." Lý luận này sai chỗ nào?▸
Sai cốt lõi: Spring Boot không thay thế Spring Framework — nó build trên Framework. Boot chỉ thêm 5 thứ: starter dependency, auto-configuration, embedded server, Actuator, default config. Mọi annotation IoC (@Service, @Autowired, @Transactional), MVC (@RestController), Data, Security đều thuộc Spring Framework.
Hệ quả thực tế: khi debug bean không inject đúng, hoặc transaction không rollback, hoặc autowiring ambiguous — bạn đang chạm vào Framework, không phải Boot. Skip Framework đồng nghĩa khi gặp bug bạn chỉ biết "thử @Component coi sao" mà không hiểu vì sao nó hoạt động.
Cách phát hiện trong code: nếu tháo spring-boot-starter và thay bằng spring-context, code bạn vẫn chạy với annotation IoC core → đó là Framework, không phải Boot. Test thử để hiểu rõ.
Q2Liệt kê 3 module Spring Framework bạn sẽ gặp khi làm 1 REST API CRUD đơn giản (controller + service + JPA repository + transaction). Mỗi module có annotation đại diện nào?▸
spring-context+spring-beans— IoC container. Annotation:@Component,@Service,@Repository,@Configuration,@Bean,@Autowired,@Value.spring-webmvc— Web layer. Annotation:@RestController,@RequestMapping,@GetMapping,@PostMapping,@PathVariable,@RequestBody,@ResponseStatus.spring-tx— Transaction. Annotation:@Transactional(trên class hoặc method service).spring-orm+ Spring Data JPA — Data access. Annotation:@Repository(alias của@Component) hoặcextends JpaRepository(Spring Data sinh implementation).
Không gặp trực tiếp nhưng có sẵn trong classpath: spring-aop (cho proxy @Transactional), spring-expression (SpEL trong @Value), spring-jdbc (under the hood của JPA).
Insight: app "đơn giản" cũng dùng 6-8 module Spring. Hiểu boundary giúp lookup docs đúng chỗ.
Q3Spring Boot có 5 thứ "đóng gói": Framework + Starter + AutoConfig + Embedded server + Actuator. Trong 5 thứ đó, thứ nào quyết định Boot có thể chạy java -jar app.jar không cần Tomcat ngoài? Thứ nào quyết định khi thêm spring-boot-starter-data-jpa vào pom.xml thì auto có EntityManager bean?▸
java -jar app.jar không cần Tomcat ngoài? Thứ nào quyết định khi thêm spring-boot-starter-data-jpa vào pom.xml thì auto có EntityManager bean?Câu 1 — chạy java -jar không cần Tomcat ngoài: Embedded server. Boot pull Tomcat (hoặc Jetty/Undertow/Netty) như thư viện, đóng gói trong jar, gọi start() programmatically lúc app boot. Không có embedded server thì jar không có HTTP listener — phải deploy vào Tomcat external như Spring 4 era.
Câu 2 — thêm starter-data-jpa thì auto có EntityManager: Auto-configuration. Cụ thể là class HibernateJpaAutoConfiguration (trong jar spring-boot-autoconfigure). Class này có @ConditionalOnClass(EntityManagerFactory.class) — chỉ register khi Hibernate có trong classpath. Starter đưa Hibernate jar vào classpath → condition true → auto-config kích hoạt → bean tạo.
Phân biệt rõ: Starter đưa jar vào classpath; AutoConfig đưa bean vào container. 2 lớp khác nhau, phối hợp.
Q4Vì sao Java EE/EJB 2.x bị Rod Johnson mô tả là "vendor lock-in"? Lock vào cái gì cụ thể? Spring giải quyết bằng cách nào?▸
Lock vào 2 thứ chính:
- Application server cụ thể: mỗi vendor (BEA WebLogic, IBM WebSphere, JBoss) có deployment descriptor riêng (
weblogic-ejb-jar.xml,ibm-ejb-jar-bnd.xml). Đổi vendor = viết lại file descriptor, đôi khi rebuild. - Container API: EJB bean phải implement
SessionBean,EntityBean— interface từjavax.ejb. Code business logic gắn cứng vào EJB API → không chạy được ngoài app server (kể cả unit test).
Spring giải bằng 2 cơ chế:
- POJO programming model: code business chỉ là
OrderServiceplain class — không extend, không implement framework class. Annotation chỉ là metadata ngoài, có thể bỏ đi mà code vẫn compile. - Embedded server thay external: Tomcat trong jar — không phụ thuộc vendor. Đổi server = đổi 1 starter dependency.
Hệ quả: code Spring chạy được mọi platform có JVM. Đổi production từ on-prem Tomcat sang AWS Elastic Beanstalk sang K8s — code không thay đổi.
Q5Bạn đang setup project Spring Boot 3.4 mới. Cần Java tối thiểu version bao nhiêu? Khoá này khuyến nghị version nào? Vì sao có khoảng cách giữa 2 con số đó?▸
Java 17 tối thiểu — Spring Framework 6.x baseline Java 17, kéo theo Spring Boot 3.x cũng 17. Build với Java thấp hơn → compile fail.
Khoá khuyến nghị Java 21. Khoảng cách 17 → 21 vì:
- Java 21 là LTS tiếp theo sau 17 (Java 25 sẽ là LTS kế tiếp).
- Spring Boot 3.2+ tận dụng Virtual Threads (JEP 444) — chỉ có từ Java 21. Cấu hình
spring.threads.virtual.enabled=truechỉ work với Java 21+. - Pattern matching đầy đủ (JEP 441), record pattern (JEP 440) chỉ ổn định ở 21.
- Performance: G1 GC + ZGC ở Java 21 cải thiện đáng kể vs 17.
Spring giữ baseline 17 vì 21 mới ra 09/2023 — enterprise cần thời gian migrate. Nhưng cho project mới năm 2026, không có lý do để chọn 17 trừ khi enterprise constraint.
Q6Khi nào Spring 6 ép buộc bạn migrate? Lỗi cụ thể bạn sẽ gặp là gì? Quá trình migrate gồm những bước nào?▸
Khi upgrade Spring 5 lên Spring 6 (hoặc Spring Boot 2.x lên 3.x). Spring 6 đổi namespace từ javax.* sang jakarta.* — do Oracle nhường thương hiệu Java EE cho Eclipse Foundation, Eclipse buộc phải đổi tên package vì không còn quyền dùng javax.
Lỗi compile cụ thể:
error: package javax.servlet does not exist
import javax.servlet.http.HttpServletRequest;
^Quá trình migrate:
- Pre-check: chạy
mvn dependency:treetìm thư viện third-party còn dùngjavax.*. Nếu có (vd library cũ chưa update), phải tìm version mới hỗ trợ Jakarta hoặc thay thế. - Bump version:
spring-boot.versiontừ 2.7.x lên 3.x,java.versiontừ 8/11 lên 17. - Replace import hàng loạt:Tool
javax.servlet.* -> jakarta.servlet.* javax.persistence.* -> jakarta.persistence.* javax.validation.* -> jakarta.validation.* javax.annotation.* -> jakarta.annotation.* (PostConstruct, PreDestroy) javax.transaction.* -> jakarta.transaction.*openrewriteautomate tốt:mvn -U org.openrewrite.maven:rewrite-maven-plugin:run -Drecipe=org.openrewrite.java.migrate.UpgradeToJava17. - Test: compile + run test suite. Lỗi runtime hay xảy ra ở Hibernate, validation, security configs.
Lưu ý: javax.sql, javax.crypto, javax.xml vẫn giữ vì chúng thuộc Java SE, không phải EE — chỉ EE namespace đổi.
Q7Trong vòng đời 1 HTTP request (section 5), nếu request bị 401 Unauthorized, lớp nào trong chain reject request? Module nào đang quản lớp đó?▸
Lớp Servlet Filter Chain reject request — cụ thể là filter BearerTokenAuthenticationFilter (hoặc UsernamePasswordAuthenticationFilter tuỳ config) trong filter chain của Spring Security.
Module quản: Spring Security (project riêng, không thuộc Framework core).
Cơ chế: Spring Security đăng ký FilterChainProxy như 1 servlet filter cấp cao. Filter này delegate đến SecurityFilterChain bean — chuỗi 10-15 filter nhỏ (CSRF, CORS, Auth, Authorization, ExceptionTranslation). Nếu 1 trong số đó reject (vd token JWT invalid), filter throw AuthenticationException, ExceptionTranslationFilter bắt và trả 401.
Vì sao request không đến DispatcherServlet hoặc @RestController: filter chain chạy trước DispatcherServlet (đó là Servlet API spec). Filter reject = response trả ngay, không qua MVC. Đó là lý do bạn không thể debug 401 bằng cách break point trong controller — chưa đến controller.
Cách debug: bật log logging.level.org.springframework.security=DEBUG để thấy filter nào reject và vì sao. Course Spring Security đào sâu.
Q8Spring Framework có 2 web stack song song: MVC và WebFlux. Khi nào chọn cái nào? Cho ví dụ cụ thể use case nên chọn WebFlux thay MVC.▸
Chọn MVC (default 95%):
- Web app CRUD bình thường, REST API standard.
- Team Java mainstream — quen imperative, debug stack trace dễ.
- Tích hợp với JDBC blocking driver (PostgreSQL, MySQL — driver chính thức blocking).
- Spring Boot 3.2+ với Virtual Threads — MVC + Virtual Thread đạt scale tương đương WebFlux mà không cần học reactive paradigm.
Chọn WebFlux:
- Streaming server-sent events (SSE) hoặc WebSocket lâu dài: vd realtime dashboard, chat, market data feed — số connection cao, mỗi connection idle lâu, WebFlux scale connection rẻ hơn.
- Aggregation từ nhiều downstream service: vd API gateway gọi 5 microservice song song, gộp kết quả. WebFlux
Mono.zip()+ non-blocking I/O hiệu quả hơn 5 thread blocking. - Backpressure-sensitive: producer nhanh hơn consumer (vd ingest IoT data) — Reactive Streams native support backpressure.
- Bytes throughput cực cao: proxy, file streaming, video — Netty + WebFlux loại bỏ overhead servlet container.
Ví dụ cụ thể: app tracking giá crypto realtime, push update đến 50,000 client qua SSE. Mỗi connection idle ~30s giữa các tick. MVC blocking: 50,000 thread = 50GB RAM (1MB/thread). WebFlux Netty: vài chục event loop thread, scale tới 100,000 connection trên server 8GB RAM.
Quy tắc: không chọn WebFlux vì "nó modern hơn". Chỉ chọn khi có lý do cụ thể đo lường được. Course Spring Reactive & Microservices sẽ đào sâu khi nào "đủ lý do".
Bài tiếp theo: IoC và DI — bóc tách 2 thuật ngữ bị lạm dụng nhất
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên