โหมดมืด
บทที่ 0 — Spring Framework คืออะไร และทำไมเรียน
(Spring = framework เขียน backend ของภาษา Java — ไม่ใช่ฤดูใบไม้ผลิ)
ก่อนจะลงโค้ด เราจะตอบคำถาม 4 ข้อ ที่ถ้าไม่เข้าใจตั้งแต่ต้น Spring จะเหมือนเวทมนตร์:
- ปัญหาของ Java ที่เขียน backend แบบ "เปล่า ๆ" คืออะไร?
- IoC (Inversion of Control — กลับด้านการควบคุม) คืออะไร — แปลให้เห็นภาพ
- DI (Dependency Injection — การฉีดของที่ต้องใช้) คืออะไร — เกี่ยวกับ IoC ยังไง
- Spring vs Spring Boot — ต่างกันยังไง
หลังจบบทคุณจะ "เข้าใจวัตถุประสงค์" ของ Spring — บทต่อ ๆ ไปจะเข้าใจง่ายขึ้นมาก
📋 ก่อนเริ่ม — ต้องผ่านอะไรมาก่อน?
- ✅ Java พื้นฐาน (Java บทที่ 1–8: class, object, interface, generics, exception)
- 🔑 ศัพท์ที่จะเจอบ่อยในบทนี้ (ไม่ต้องท่อง เดี๋ยวอธิบายระหว่างทาง):
- dependency (ดีเพน-เดน-ซี) = "สิ่งที่ของชิ้นหนึ่งต้องใช้" เช่น
UserServiceต้องใช้UserRepository→UserRepositoryคือ dependency ของUserService- container (คอนเทนเนอร์) = "กล่องเก็บของ" ที่ Spring ใช้เก็บ object ทั้งหมดไว้ + สร้าง/ส่งให้กัน
- inject (อิน-เจกต์) = "ฉีด/ยัดเข้าไป" — เอา object หนึ่งส่งเข้าไปให้อีก object ใช้
- annotation (แอนโน-เท-ชัน) = ป้ายกำกับที่เขียนนำหน้าด้วย
@เช่น@Componentเป็นการ "บอกใบ้" ให้ Spring รู้ว่าต้องทำอะไรกับ class นี้
1. ปัญหา: เขียน backend Java แบบ "เปล่า ๆ" คือยังไง
สมมติเราจะเขียนระบบเล็ก ๆ — มี API ให้ดึงข้อมูล user:
java
class UserService {
UserRepository repo;
EmailService email;
AuditLogger logger;
User getUser(String id) {
logger.log("get user " + id);
return repo.findById(id);
}
void sendWelcome(User u) {
email.send(u.getEmail(), "Welcome");
logger.log("sent welcome to " + u.getEmail());
}
}UserService ต้องใช้ UserRepository, EmailService, AuditLogger — เรียก dependencies
ทำไงให้ UserService มี dependency เหล่านี้?
วิธีที่ 1: สร้างเอง
java
class UserService {
UserRepository repo = new UserRepository();
EmailService email = new EmailService();
AuditLogger logger = new AuditLogger();
}ปัญหา:
- Coupling (คับ-พลิ่ง — การผูกติด/พึ่งพากันแน่น) — UserService รู้ว่า UserRepository ต้อง new ยังไง (แต่ในงานจริง UserRepository ต้องการ database connection ที่ต้องการ config — UserService ไม่ควรรู้)
- Test ยาก — อยากเปลี่ยน UserRepository เป็น mock ใน test ทำไม่ได้
- ขยายยาก (Hard to extend) — เปลี่ยน EmailService → MockEmailService สำหรับ dev ทำไม่ได้
วิธีที่ 2: ส่งผ่าน constructor (better)
java
class UserService {
UserRepository repo;
EmailService email;
AuditLogger logger;
UserService(UserRepository repo, EmailService email, AuditLogger logger) {
this.repo = repo;
this.email = email;
this.logger = logger;
}
}ใช้:
java
// pseudo-code — ดูไอเดียพอ ไม่ต้องพิมพ์ตาม
// ต้องสร้าง dependency (สิ่งที่ต้องใช้) ก่อน
DataSource ds = createDataSource(...); // DataSource = ตัวเชื่อมต่อ database
UserRepository repo = new UserRepository(ds);
EmailConfig emailCfg = loadEmailConfig(...);
EmailService email = new EmailService(emailCfg);
AuditConfig auditCfg = loadAuditConfig(...);
AuditLogger logger = new AuditLogger(auditCfg);
// ค่อยสร้าง service (ตัวที่มี business logic)
UserService service = new UserService(repo, email, logger);ปัญหาใหม่:
- โค้ดที่ "wire dependency" (ต่อสายให้ object รู้ว่าใครต้องใช้ใคร) ยาว + ซับซ้อน
- ถ้ามี 100 service → wiring code ยาว 1000+ บรรทัด
- ลำดับสร้างผิดง่าย
- เปลี่ยน implementation 1 ตัว → แก้หลายที่
นี่คือปัญหาที่ Spring แก้
Spring เป็น container ที่:
- เก็บ object (dependency) ทั้งหมดไว้
- รู้ว่า object แต่ละตัวต้องการอะไร
- สร้างให้ + inject ให้ อัตโนมัติ
- เราแค่บอก "เนี่ย UserService ต้องการ UserRepository" — Spring ทำที่เหลือ
2. IoC — Inversion of Control
IoC = "Inversion of Control" — กลับด้านการควบคุม
แบบเดิม (ไม่มี IoC)
โค้ดของเรา ควบคุม การสร้าง dependency:
java
class UserService {
UserRepository repo = new UserRepository(); // ฉัน (โค้ดเรา) สร้างเอง
}ใครเลือก implementation? — UserService
แบบ IoC
โค้ดของเรา ขอ dependency จาก framework:
java
class UserService {
UserRepository repo;
UserService(UserRepository repo) {
this.repo = repo; // ใครก็ได้ส่งมาให้ (เราไม่ได้สร้างเอง)
}
}ใครเลือก implementation? — Framework (Spring) — "ฉัน framework เลือกให้คุณ"
คำว่า "inverted": ปกติ class A เรียก B → ตอนนี้ B ถูกส่งให้ A — กลับด้าน
Analogy: ร้านอาหาร vs ทำกินเอง
- ไม่มี IoC = ทำกินเอง → ต้องไปซื้อของ ปอกหั่น ปรุง ล้างจาน → คุมทุกขั้น แต่เหนื่อย
- มี IoC = ไปร้าน → บอกว่า "ขอข้าวผัด" → ครัวจัดการให้ → คุณกินอย่างเดียว
- คุณบอก "ขอ" (declare dependency) ไม่ใช่ "ทำเอง" (instantiate)
- ร้าน (Spring) ตัดสินใจว่าใช้กระทะอันไหน เชฟคนไหน
อีก analogy คลาสสิกคือ "Don't call us, we'll call you" (อย่าโทรหาเรา เดี๋ยวเราโทรหาคุณ) — เรียกว่า Hollywood Principle หมายความว่า framework เป็นฝ่ายเรียกโค้ดเรา ไม่ใช่เราเรียกมัน เปรียบได้กับตอนสัมภาษณ์งาน — HR เป็นฝ่ายโทรหาคุณเอง ไม่ใช่คุณต้องโทรไปถามผลสัมภาษณ์ คุณแค่รอ (declare) ว่าพร้อม
Framework vs Library — ความต่างที่สำคัญ
ใหม่ๆ คนสับสน 2 คำนี้:
| Library | Framework | |
|---|---|---|
| ใครเรียกใคร | คุณเรียก library | Framework เรียกคุณ (callbacks — โค้ดของคุณที่ Spring เรียกให้ เช่น method hello() ที่คุณเขียนแต่ Spring เป็นคนเรียกตอนมี HTTP request เข้ามา) |
| ใครคุม flow | คุณ | Framework |
| ตัวอย่าง | Apache Commons, Jackson, Guava | Spring, Angular, Spring Boot |
Spring = framework เพราะมัน เรียก method ของคุณ ตอนที่มันเลือก — ไม่ใช่คุณเรียก Spring โดยตรง
ภาพ:
3. DI — Dependency Injection
DI = "Dependency Injection" — วิธีหนึ่งของ IoC
IoC เป็นแนวคิดกว้าง ๆ ว่า "ใครคุมการสร้าง object" — มีหลายวิธีทำ DI เป็นวิธีที่นิยมที่สุด (วิธีอื่นเช่น Service Locator มี แต่ Spring ใช้ DI เป็นหลัก)
DI = "ฉีด dependency เข้าไปใน object แทนที่จะให้มันหาเอง"
3 รูปแบบ DI ที่ Spring รองรับ
📖
@Autowiredคืออะไร (กางก่อนเข้าโค้ดข้างล่าง): ป้ายบอก Spring ว่า "ฉีด object ที่ตรงกับ type (ประเภทข้อมูล เช่น UserRepository, EmailService) นี้เข้ามาให้หน่อย" — Spring จะหาว่า object ไหนใน container (เดี๋ยว §4 จะเรียกสิ่งนี้ว่า bean) มี type ตรงกัน แล้วเอามาใส่ให้ (ไม่ต้องnewเอง) วางได้บน constructor, setter, หรือ field — แต่บน constructor ดีที่สุด
3.1 Constructor injection (แนะนำ)
💡 Spring 4.3+: ถ้ามี constructor เดียว ไม่ต้องเขียน
@Autowiredก็ได้ (Spring ฉีดให้อัตโนมัติ) — หมายเหตุ: เป็น Spring version ไม่ใช่ Java version
java
class UserService {
final UserRepository repo;
@Autowired
UserService(UserRepository repo) {
this.repo = repo;
}
}ข้อดี:
- field เป็น
finalได้ — immutable - บังคับ dependency ทั้งหมดตอน new
- test ง่าย — แค่ส่ง mock ผ่าน constructor
3.2 Setter injection
java
class UserService {
UserRepository repo;
@Autowired
void setRepo(UserRepository repo) {
this.repo = repo;
}
}ใช้น้อย — ไม่บังคับ dependency, field ไม่ final
3.3 Field injection (ไม่แนะนำ)
java
class UserService {
@Autowired
UserRepository repo;
}ดูเหมือนสั้น — แต่:
- 🚫 NEVER use in new code — Spring team ไม่แนะนำ และ IDE อย่าง IntelliJ จะขีดเส้นเตือน "Field injection is not recommended" (Spring เองไม่ throw error แต่ชุมชนหลีกเลี่ยงและจะผ่าน code review ได้ยาก)
- ❌ test ยาก (ต้อง reflection)
- ❌ field ไม่ final ได้ (mutability problem)
- ❌ ไม่เห็น dependency ใน constructor (hidden)
กฎทอง: ใช้ constructor injection เสมอ
4. Bean — object ที่ Spring จัดการ
ใน Spring, object ที่ container เก็บไว้เรียกว่า bean
📖 อะไรคือ
@SpringBootApplication? — ป้ายที่วางบน main class ของแอป มันเปิด Spring container + auto-config ให้ครบในตัวเดียว (รายละเอียดเต็มในบทที่ 1)📖 lifecycle (ไลฟ์-ไซ-เคิล) = วงจรชีวิตของ bean: สร้าง → inject → ใช้งาน → ทำลายตอน app ปิด — Spring คุมทั้งหมดให้
java
@Component // บอก Spring ว่า "นี่คือ bean จัดการให้ที"
class UserService {
final UserRepository repo;
UserService(UserRepository repo) {
this.repo = repo;
}
}
@Component
class UserRepository { ... }Spring จะ:
- Scan หา class ที่มี
@Component - สร้าง instance (เรียก constructor)
- เก็บใน "container"
- ตอนสร้าง
UserService— เห็นว่ารับUserRepositoryใน constructor → หา bean ของUserRepository→ ส่งให้
💡 ใคร scan? —
@SpringBootApplicationบน main class จะ scan ตั้งแต่ package ของ main class ลงไปทั้งหมด อัตโนมัติ — วาง class ใน sub-package ก็เจอ แต่ถ้าวางนอก package tree นั้นจะไม่เจอ
Annotation แบบเฉพาะของ @Component
@Component— ทั่วไป@Service— สำหรับ business logic@Repository— สำหรับ data access (โค้ดติดต่อ database) มีของเสริมชื่อ exception translation (การแปลง error จาก DB ให้เป็นรูปแบบมาตรฐานของ Spring):- เวลา database ขัดข้อง
@Repositoryจะแปลง error ให้เป็นDataAccessExceptionมาตรฐาน ไม่ว่าจะใช้ MySQL หรือ PostgreSQL - ผลคือเปลี่ยน DB ก็ไม่ต้องแก้ catch — รายละเอียดเต็มในบทที่ 2
- เวลา database ขัดข้อง
@Controller— สำหรับ web controller (return ชื่อ template เพื่อให้ server ประกอบเป็นหน้า HTML ส่งกลับ — หนังสือเล่มนี้เน้น REST API จึงแทบไม่ได้ใช้@Controllerแบบนี้)@RestController—@Controller+@ResponseBody(= บอกว่า return value ให้แปลงเป็น JSON ส่งกลับ ไม่ต้อง render หน้า HTML) เหมาะกับ REST API ที่คืน JSON ตรง ๆ
ทุกตัวคือ @Component เหมือนกัน — แค่ระบุ "บทบาท" ให้ชัด
5. Spring vs Spring Boot
Spring Framework (เก่า — ยังใช้)
- container + DI
- abstraction สำหรับ web, data, security, ฯลฯ
- ปัญหา: configure ยุ่งยาก — XML config ยาว, ต้อง set up Tomcat, DispatcherServlet ฯลฯ
ตัวอย่าง Spring แบบเก่า (XML):
xml
<beans>
<bean id="userRepo" class="com.example.UserRepository">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="userService" class="com.example.UserService">
<constructor-arg ref="userRepo"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="url" value="jdbc:postgresql://localhost/db"/>
<property name="username" value="admin"/> <!-- ⚠️ ในงานจริงห้าม hardcode credential ตรงนี้ — ใช้ environment variable หรือ secrets vault แทน -->
</bean>
</beans>ปวดหัว 😩
Spring Boot (2014+, ใช้ปัจจุบัน)
- ครอบ Spring + opinionated (โอ-พิ-เนีย-เนเทด — "มีความเห็นของตัวเอง" คือ framework เลือก default ที่ดีไว้ให้แล้ว เราไม่ต้องตั้งค่าเองทุกอย่าง)
- ไม่มี XML — ใช้ annotation + properties file
- Embedded server (เซิร์ฟเวอร์ฝังในตัว) — รวม Tomcat (โปรแกรม web server ที่รับ HTTP) มาในไฟล์ jar เดียวกัน (
java -jar app.jarก็ run server แล้ว) - Starter dependency (แพ็คคู่ของ library — แปะตัวเดียวได้ทุกอย่างที่ต้องคู่กัน) —
spring-boot-starter-webแปะตัวเดียวได้ทั้ง web + JSON + validation - Auto-configuration — Spring เดาให้ว่าต้องการอะไรจาก classpath (รายการ jar/ไฟล์
.classที่ Java โหลดมาเปิดใช้งานตอนรัน — เหมือน PATH ของ OS แต่ใช้กับ Java)
ตัวอย่าง Spring Boot:
java
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
@RestController
class HelloController {
@GetMapping("/")
String hello() { return "Hello, Spring!"; }
}2 class — server พร้อม run
Convention เทียบสั้น ๆ
| Spring (เก่า) | Spring Boot | |
|---|---|---|
| Config | XML / Java config | annotation + application.properties |
| Server | configure Tomcat แยก | embedded — run จาก main |
| Dependency | เลือก library ทีละตัว | starter — group เลย |
| Setup | ใช้เวลา | start.spring.io 30 วินาที (เว็บ generate project สำเร็จรูป — จะใช้จริงในบทที่ 1) |
ปี 2026 — ใช้ Spring Boot เป็นค่าเริ่มต้น
6. Spring Boot ทำอะไรให้ฟรี
ตอน start app:
- Scan annotation (
@Component,@Serviceฯลฯ) → สร้าง bean - Inject dependency อัตโนมัติ
- Detect dependency บน classpath → auto-configure
- เห็น
spring-boot-starter-data-jpa→ configure DataSource (ตัวจัดการ connection ไป DB) + EntityManager (ตัวจัดการ object ↔ DB mapping) — รายละเอียดบทที่ 2 - เห็น
spring-boot-starter-web→ start embedded Tomcat
- เห็น
- Map URL → controller method
- Convert JSON ↔ object อัตโนมัติ (ผ่าน library ชื่อ Jackson ที่ Spring Boot แถมมาให้)
- Manage transaction (
@Transactional— รายละเอียดในบทที่ 2) - Caching (บทที่ 11), scheduling (บทที่ 13), async — ถ้าเปิด
ทั้งหมดนี้ developer แค่บอก "ฉันต้องการอะไร" — Spring จัดการ "ยังไง"
7. ภาพรวม project structure
text
my-app/
├── pom.xml ← Maven config
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/
│ │ │ ├── Application.java ← main()
│ │ │ ├── controller/
│ │ │ │ └── UserController.java ← @RestController
│ │ │ ├── service/
│ │ │ │ └── UserService.java ← @Service
│ │ │ ├── repository/
│ │ │ │ └── UserRepository.java ← @Repository
│ │ │ └── model/
│ │ │ └── User.java ← entity / record
│ │ └── resources/
│ │ ├── application.properties ← config
│ │ └── static/ ← static files (CSS, JS)
│ └── test/
│ └── java/ ← tests
└── target/ ← compile output (auto)Layer
Controller → Service → Repository → Database
- Controller — รับ HTTP request, แปลง JSON
- Service — business logic
- Repository — เข้าถึง database
- ไม่ข้าม layer (controller อย่าเรียก repository ตรง)
8. ทำไม Spring Boot คือ "the framework" ของ Java backend
- Mature — 10 ปี + ใช้ใน Netflix, ธนาคาร, government
- Ecosystem — Spring Data, Spring Security, Spring Cloud (microservices), Spring Batch ฯลฯ
- Documentation — เยอะ + ดี
- Community — Stack Overflow ตอบครบ
- Job market — Java backend = Spring Boot 90%
ปี 2026:
- Spring Boot 3.x = Java 17 ขั้นต่ำ บน Spring Framework 6.x; 3.2+ เพิ่มการรองรับ Virtual Threads (เหมาะสุดกับ Java 21); 3.4+ แนะนำสำหรับ project ใหม่ — หนังสือเล่มนี้ใช้ Java 21 ตลอด ติดตั้ง Java 21 ก่อนเริ่มบทที่ 1
- รองรับ feature ใหม่ ๆ ของ JVM (เช่น Virtual Threads, GraalVM Native Image, Reactive/WebFlux) — รายละเอียดบทที่ 10 และ 15 ตอนนี้ยังไม่ต้องสนใจ
- (last reviewed: 2026-06 — ตรวจ release notes ทางการก่อนใช้)
🟡 สำหรับคนที่เคย deploy Spring Boot 2 มาก่อน — Jakarta EE migration (ข้ามได้ถ้าเพิ่งเริ่ม)
- Spring Boot 3.x ย้าย namespace (กลุ่ม package ของ Java) จาก
javax.*→jakarta.*ทุก import (Servlet, Persistence, Validation, Annotation, Transaction ฯลฯ)- ทุก library ที่ใช้ใน app ต้องรองรับ Jakarta — Hibernate ≥6, Tomcat ≥10, Jersey ≥3
- migration tool:
eclipse-transformerช่วย rewrite ทั้ง project, แต่ต้อง verify ทุก dependency- ถ้าอ่าน Stack Overflow หรือ blog เก่าที่ใช้
javax.persistence.*ต้องแปลเป็นjakarta.persistence.*ทุกครั้ง- ถ้าเริ่ม project ใหม่ในปี 2026 — ข้ามได้ ส่วนนี้สำหรับคนย้าย project เก่าเท่านั้น
📚 8.1 ApplicationContext vs BeanFactory + 8.2 Bean scope (อ่านได้ตอนคล่องแล้ว — บทที่ 0 ผ่านได้โดยไม่ต้องเข้าใจส่วนนี้)
8.1 ApplicationContext vs BeanFactory
"Spring container" ที่บทนี้พูดถึงจริง ๆ มี 2 ระดับ:
| Interface | คืออะไร | ใช้เมื่อไหร่ |
|---|---|---|
BeanFactory | container ระดับล่างสุด — รู้จัก bean lazy + DI พื้นฐานเท่านั้น | rarely used directly (รากของทุก context) |
ApplicationContext | extends BeanFactory — เพิ่ม eager init, AOP (การเพิ่มโค้ดข้ามหลาย class แบบอัตโนมัติ), event publishing (การแพร่เหตุการณ์ภายใน app), i18n (internationalization = รองรับหลายภาษา), environment abstraction (ชั้นซ่อนความแตกต่างของ config แต่ละ environment), resource loading (การโหลดไฟล์/resource) | ใช้ตัวนี้ตลอด — Spring Boot สร้างให้อัตโนมัติ |
Spring Boot สร้าง ApplicationContext ให้ตอน SpringApplication.run(...) — bean ทั้งหมดเข้าไปอยู่ใน context นี้
java
@SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args);
// ctx.getBean(UserService.class) — เข้าถึง bean ผ่าน context ได้ (rare)
}
}8.2 Bean scope + thread-safety
bean default คือ singleton (1 instance ต่อ container) — ดี สำหรับ stateless service แต่ อันตราย ถ้าเก็บ mutable state ใน field เพราะหลาย thread เข้าพร้อมกัน:
| Scope | ทำงาน |
|---|---|
singleton (default) | 1 instance — ห้ามเก็บ mutable state (ข้อมูลที่เปลี่ยนได้) ใน field |
prototype | สร้างใหม่ทุกครั้งที่ inject |
request (web) | 1 instance ต่อ HTTP request |
session (web) | 1 instance ต่อ HTTP session |
java
@Service
@Scope("singleton") // implicit default
public class UserService {
private final UserRepository repo; // ✅ final + immutable — thread-safe
private List<User> cache = new ArrayList<>(); // ❌ mutable field ใน singleton = race condition
// constructor + methods ถูกตัดออกเพื่อความกระชับ — ตัวอย่างนี้เน้นแสดงเฉพาะประเด็น thread-safety
}ศัพท์ multithreading ที่ใช้ในกล่องนี้ (ดู Java บทที่ 9):
- mutable state = ข้อมูลที่เปลี่ยนได้
- race condition = สถานการณ์ที่ 2 thread แก้ของพร้อมกันแล้วผลปนกัน
- stateless = ไม่เก็บข้อมูลใน field
- thread-safe = ปลอดภัยเมื่อหลาย thread ใช้พร้อมกัน
9. Checkpoint ความเข้าใจ
🛠️ Checkpoint 0.1 — ทบทวน
ลองตอบในใจ:
- IoC คืออะไร ในประโยคเดียว
- DI ต่างจาก IoC ยังไง
- ทำไม constructor injection ดีกว่า field injection
- Bean คืออะไร
- Spring vs Spring Boot ต่างกันอย่างไร
📖 เฉลย
- กลับด้านการควบคุม — แทนที่ class จะสร้าง dependency เอง, framework สร้างและฉีดให้
- DI เป็น "วิธีหนึ่ง" ของ IoC — ใช้การฉีด dependency ผ่าน constructor/setter/field
- constructor: field final ได้, test ง่าย (mock ส่งผ่าน constructor), บังคับ dependency ตอน new
- object ที่ Spring container จัดการ — สร้างให้, inject ให้, จัดการ lifecycle
- Spring = framework core (DI + AOP + ฯลฯ); Spring Boot ครอบ Spring + opinionated config + embedded server
🛠️ Checkpoint 0.2 — ออกแบบ
มีโจทย์: สร้างระบบจัดการคำสั่งซื้อ มี components:
OrderController— รับ HTTPOrderService— business logicOrderRepository— เข้าถึง DBPaymentClient— เรียก external APINotificationService— ส่ง email
ถ้าใช้ Spring จะมี dependency อะไรเข้าใครยังไง?
📖 เฉลย
OrderControllerขอOrderServiceผ่าน constructorOrderServiceขอOrderRepository,PaymentClient,NotificationServiceผ่าน constructor
Spring เห็น @Component ทุกตัว → สร้างให้ → inject ให้ — ไม่ต้องเขียน new เอง
10. สรุปบท
✅ ปัญหา: เขียน Java เปล่า ๆ → coupling สูง, test ยาก, wiring code ยาว ✅ IoC = framework ควบคุมการสร้าง object แทนเรา ✅ DI = วิธีของ IoC — ฉีด dependency ผ่าน constructor (แนะนำ) / setter / field ✅ Bean = object ที่ Spring container เก็บไว้ ✅ @Component / @Service / @Repository / @Controller / @RestController = annotation บอก Spring ✅ Spring Boot ครอบ Spring + auto-configuration + embedded server + starter dependency ✅ Layer: Controller → Service → Repository — ไม่ข้าม
→ ไปบทที่ 1: First Spring Boot App
🔤 Glossary · 📋 Style guide · 📅 last_verified: 2026-06-23