Giới thiệu về Lập trình Web & Spring Boot


Table of Contents

Web hoạt động như thế nào — Chu trình Request/Response của HTTP

Trước khi viết bất kỳ dòng code Spring Boot nào, bạn cần hiểu điều gì thực sự xảy ra khi bạn gõ một URL vào trình duyệt và nhấn Enter. Đây là nền tảng của mọi thứ bạn sẽ xây dựng với tư cách là một lập trình viên web.

Hành trình của một Web Request

Hãy tưởng tượng bạn mở trình duyệt và gõ https://www.google.com. Đây là những gì xảy ra phía sau:

Bước 1: Phân giải DNS

Trình duyệt của bạn không biết www.google.com nằm ở đâu. Nó cần một địa chỉ IP — một địa chỉ dạng số như 142.250.80.4. Vì vậy, nó hỏi một máy chủ DNS (Domain Name System — Hệ thống Tên miền) để dịch tên miền dễ đọc thành một địa chỉ IP. Hãy nghĩ DNS như một cuốn danh bạ điện thoại của internet.

Bước 2: Kết nối TCP

Khi trình duyệt biết được địa chỉ IP, nó thiết lập một kết nối TCP (Transmission Control Protocol — Giao thức Điều khiển Truyền tải) với máy chủ. Điều này giống như thực hiện một cuộc gọi điện thoại — cả hai bên đồng ý bắt đầu giao tiếp.

Bước 3: HTTP Request

Bây giờ trình duyệt gửi một HTTP request đến máy chủ. HTTP là viết tắt của HyperText Transfer Protocol (Giao thức Truyền tải Siêu văn bản) — đây là ngôn ngữ mà trình duyệt và máy chủ sử dụng để giao tiếp với nhau.

Một HTTP request đơn giản trông như thế này:

GET /search?q=spring+boot HTTP/1.1
Host: www.google.com
Accept: text/html
User-Agent: Mozilla/5.0

Hãy phân tích từng dòng:

  • GET — Đây là phương thức HTTP (HTTP method). GET có nghĩa là “Tôi muốn lấy một thứ gì đó.” Có các phương thức khác như POST (gửi dữ liệu), PUT (cập nhật dữ liệu), và DELETE (xóa dữ liệu). Chúng ta sẽ sử dụng tất cả các phương thức này trong suốt series.
  • /search?q=spring+boot — Đây là đường dẫn (path) (còn gọi là URI). Nó cho máy chủ biết chính xác tài nguyên bạn muốn. Phần sau dấu ? là một tham số truy vấn (query parameter).
  • HTTP/1.1 — Đây là phiên bản giao thức.
  • Host, Accept, User-Agent — Đây là các header. Chúng cung cấp thông tin bổ sung về request, như website bạn muốn truy cập, định dạng bạn có thể chấp nhận, và trình duyệt bạn đang sử dụng.

Bước 4: Server xử lý

Máy chủ nhận request, xử lý nó (chạy một số code, có thể truy vấn cơ sở dữ liệu), và chuẩn bị một response.

Bước 5: HTTP Response

Máy chủ gửi lại một HTTP response:

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 12345

<!DOCTYPE html>
<html>
  <body>
    <h1>Search Results</h1>
    ...
  </body>
</html>

Đây là ý nghĩa của từng phần:

  • 200 OK — Đây là mã trạng thái (status code). 200 có nghĩa là mọi thứ đã diễn ra tốt đẹp. Bạn sẽ gặp nhiều mã trạng thái khác: 404 (không tìm thấy), 500 (lỗi máy chủ), 201 (đã tạo), 401 (chưa xác thực), và nhiều hơn nữa.
  • Content-Type: text/html — Header này cho trình duyệt biết nội dung response là HTML.
  • Phần còn lại là phần thân response (response body) — nội dung thực tế (HTML, JSON, hình ảnh, v.v.).

Bước 6: Trình duyệt hiển thị

Trình duyệt nhận HTML và hiển thị nó trên màn hình của bạn. Nếu HTML tham chiếu đến các file CSS, file JavaScript, hoặc hình ảnh, trình duyệt sẽ gửi thêm các HTTP request để lấy chúng.

Tại sao điều này quan trọng với bạn

Với tư cách là một lập trình viên Spring Boot, công việc của bạn là viết code chạy ở Bước 4 — phần xử lý của server. Bạn sẽ viết code để:

  • Lắng nghe các HTTP request đến
  • Đọc phương thức, đường dẫn, header và body của request
  • Thực thi logic nghiệp vụ (tính toán, truy vấn cơ sở dữ liệu, kiểm tra hợp lệ)
  • Trả về một HTTP response với mã trạng thái và body phù hợp

Đây là cốt lõi của lập trình web. Mọi thứ khác — framework, cơ sở dữ liệu, bảo mật — là các công cụ giúp bạn làm điều này hiệu quả hơn.


Tổng quan kiến trúc Client-Server

Bây giờ bạn đã hiểu chu trình request/response của HTTP, hãy phóng to ra và nhìn bức tranh lớn hơn: kiến trúc client-server.

Client là gì?

Client là bất kỳ ứng dụng nào gửi request đến server. Client phổ biến nhất là trình duyệt web (Chrome, Firefox, Safari). Nhưng client cũng có thể là:

  • Một ứng dụng di động (Android, iOS)
  • Một server khác (giao tiếp server-to-server)
  • Một công cụ dòng lệnh như curl
  • Một ứng dụng desktop
  • Một framework frontend JavaScript (React, Angular, Vue)

Server là gì?

Server là một ứng dụng lắng nghe các request đến, xử lý chúng, và gửi lại response. Trong trường hợp của chúng ta, server sẽ là một ứng dụng Spring Boot chạy trên một máy tính (laptop của bạn trong quá trình phát triển, một server cloud trong môi trường production).

Phân tách mối quan tâm (Separation of Concerns)

Vẻ đẹp của kiến trúc client-server nằm ở sự phân tách mối quan tâm:

  • Client chịu trách nhiệm về giao diện người dùng — hiển thị dữ liệu, xử lý tương tác, và làm cho trải nghiệm trông đẹp mắt.
  • Server chịu trách nhiệm về logic nghiệp vụ, lưu trữ dữ liệu, bảo mật, và cung cấp dữ liệu cho client.

Sự phân tách này có nghĩa là bạn có thể có nhiều loại client (ứng dụng web, ứng dụng di động, và ứng dụng desktop) tất cả đều giao tiếp với cùng một server. Server không quan tâm client trông như thế nào — nó chỉ nhận request và gửi response.

Cách chúng giao tiếp

Trong lập trình web hiện đại, định dạng giao tiếp phổ biến nhất giữa client và server là JSON (JavaScript Object Notation). Thay vì trả về HTML từ server, bạn trả về dữ liệu JSON, và client quyết định cách hiển thị nó.

Đây là một ví dụ. Client gửi một request:

GET /api/users/42 HTTP/1.1
Host: myapp.com
Accept: application/json

Server phản hồi bằng JSON:

{
  "id": 42,
  "name": "Alice",
  "email": "alice@example.com",
  "role": "ADMIN"
}

Client (dù là ứng dụng web React hay ứng dụng Android) nhận JSON này và hiển thị theo cách nó muốn. Ứng dụng React có thể hiển thị nó trong một bảng. Ứng dụng Android có thể hiển thị nó trong một card. Cùng dữ liệu, cách trình bày khác nhau.

Mô hình này — nơi server cung cấp dữ liệu thông qua API và client xử lý phần trình bày — được gọi là REST API (Representational State Transfer). Bạn sẽ xây dựng REST API trong suốt series này.

So sánh nhanh với kinh nghiệm Java của bạn

Nếu bạn đã xây dựng ứng dụng desktop Java (Swing, JavaFX), bạn viết mọi thứ ở một nơi — code giao diện và logic đều nằm trong cùng một dự án. Trong lập trình web, chúng ta tách chúng ra:

Ứng dụng DesktopỨng dụng Web
UI + Logic trong một dự ánClient (frontend) và Server (backend) tách biệt
Chạy trên máy người dùngServer chạy từ xa, client chạy trong trình duyệt
Gọi phương thức trực tiếpGiao tiếp qua HTTP
Đối tượng Java trong bộ nhớDữ liệu trao đổi dưới dạng JSON

Trong series này, chúng ta tập trung vào phía server — xây dựng backend Spring Boot cung cấp REST API. Client có thể là bất kỳ thứ gì: trình duyệt, Postman (công cụ kiểm thử API), hoặc một framework frontend.


Web Framework là gì và tại sao chúng ta cần nó?

Cách làm khó: Server HTTP Java thuần

Java có sẵn một HTTP server. Về mặt kỹ thuật, bạn có thể viết ứng dụng web mà không cần framework nào:

import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import java.io.*;
import java.net.InetSocketAddress;

public class RawServer {
    public static void main(String[] args) throws IOException {
        // Tạo một HTTP server lắng nghe trên cổng 8080
        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
        
        // Định nghĩa handler cho đường dẫn "/hello"
        server.createContext("/hello", new HttpHandler() {
            @Override
            public void handle(HttpExchange exchange) throws IOException {
                // Chuẩn bị response
                String response = "{\"message\": \"Hello, World!\"}";
                
                // Thiết lập response header
                exchange.getResponseHeaders().set("Content-Type", "application/json");
                
                // Gửi response với mã trạng thái 200
                exchange.sendResponseHeaders(200, response.getBytes().length);
                
                // Ghi phần thân response
                OutputStream os = exchange.getResponseBody();
                os.write(response.getBytes());
                os.close();
            }
        });
        
        // Khởi động server
        server.start();
        System.out.println("Server đang chạy trên cổng 8080");
    }
}

Code này tạo một HTTP server đơn giản trả về thông báo JSON khi bạn truy cập http://localhost:8080/hello. Nó hoạt động, nhưng hãy nhìn tất cả những thứ bạn phải làm thủ công:

  • Tạo server và gán nó vào một cổng
  • Đăng ký mỗi đường dẫn URL với một handler
  • Thiết lập header thủ công
  • Chuyển đổi response thành bytes thủ công
  • Quản lý output stream thủ công

Bây giờ hãy tưởng tượng bạn cần xây dựng một ứng dụng thực tế với 50 endpoint, truy cập cơ sở dữ liệu, xác thực, kiểm tra đầu vào, xử lý lỗi, ghi log, và nhiều hơn nữa. Bạn sẽ mất nhiều tháng viết code boilerplate trước khi thậm chí bắt đầu làm logic nghiệp vụ thực sự.

Cách làm dễ: Sử dụng Framework

Một web framework là tập hợp các công cụ và quy ước được xây dựng sẵn, xử lý các phần lặp đi lặp lại của lập trình web để bạn có thể tập trung vào những gì làm cho ứng dụng của bạn khác biệt.

Đây là cùng endpoint “Hello World” nhưng viết bằng Spring Boot:

@RestController
public class HelloController {

    @GetMapping("/hello")
    public Map<String, String> hello() {
        return Map.of("message", "Hello, World!");
    }
}

Chỉ vậy thôi. Ba dòng code thực tế. Spring Boot xử lý mọi thứ còn lại:

  • Khởi động HTTP server
  • Điều hướng request đến đúng phương thức
  • Chuyển đổi Map thành JSON tự động
  • Thiết lập header Content-Type chính xác
  • Quản lý output stream cho response

Đừng lo lắng nếu các annotation (@RestController, @GetMapping) trông lạ lẫm. Chúng ta sẽ giải thích từng cái một cách chi tiết. Điểm quan trọng ở đây là cho bạn thấy tại sao framework tồn tại — chúng loại bỏ code boilerplate và cho phép bạn tập trung vào logic nghiệp vụ.

Một Web Framework thường cung cấp những gì

Hầu hết các web framework, bao gồm Spring Boot, cung cấp các tính năng sau ngay khi sử dụng:

  • Routing: Ánh xạ URL đến các phương thức xử lý cụ thể
  • Xử lý Request/Response: Phân tích dữ liệu đến và định dạng dữ liệu đi
  • Truy cập cơ sở dữ liệu: Công cụ để đọc và ghi dữ liệu từ cơ sở dữ liệu
  • Bảo mật: Xác thực (bạn là ai?) và phân quyền (bạn được phép làm gì?)
  • Kiểm tra hợp lệ (Validation): Kiểm tra dữ liệu đầu vào đáp ứng yêu cầu
  • Xử lý lỗi: Trả về thông báo lỗi có ý nghĩa
  • Kiểm thử (Testing): Công cụ để kiểm thử ứng dụng mà không cần triển khai
  • Cấu hình: Quản lý thiết lập cho các môi trường khác nhau (development, staging, production)

Giới thiệu hệ sinh thái Spring

Lược sử

Spring Framework được tạo bởi Rod Johnson vào năm 2003. Vào thời điểm đó, xây dựng ứng dụng web Java đồng nghĩa với việc sử dụng Java EE (Enterprise Edition), vốn nổi tiếng là phức tạp và nặng nề. Spring ra đời như một giải pháp thay thế nhẹ hơn, đơn giản hơn.

Qua nhiều năm, Spring phát triển thành một hệ sinh thái khổng lồ với nhiều dự án con. Đây là những dự án bạn sẽ gặp thường xuyên nhất:

  • Spring Framework — Nền tảng cốt lõi. Cung cấp Dependency Injection (IoC container), AOP (Aspect-Oriented Programming — Lập trình hướng khía cạnh), và hỗ trợ web cơ bản.
  • Spring MVC — Tầng web. Xử lý HTTP request và response sử dụng mô hình Model-View-Controller.
  • Spring Data — Đơn giản hóa việc truy cập cơ sở dữ liệu. Spring Data JPA (mà chúng ta sẽ sử dụng với MariaDB) cho phép bạn làm việc với cơ sở dữ liệu sử dụng các đối tượng Java thay vì SQL thuần.
  • Spring Security — Xử lý xác thực và phân quyền.
  • Spring Boot — Bước ngoặt lớn. Giúp việc tạo ứng dụng Spring trở nên cực kỳ dễ dàng với cấu hình tối thiểu.

Spring Framework vs Spring Boot

Sự khác biệt này rất quan trọng, và nhiều người mới học thường nhầm lẫn hai khái niệm.

Spring Framework là công nghệ nền tảng — động cơ. Nó cung cấp các tính năng mạnh mẽ nhưng đòi hỏi cấu hình thủ công đáng kể. Ngày xưa, bạn phải viết hàng trăm dòng XML để cấu hình một ứng dụng Spring:

<!-- Cấu hình Spring kiểu cũ (bạn KHÔNG cần viết thế này) -->
<beans>
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    </bean>
    
    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="com.example.model"/>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>
    
    <!-- ... và còn nhiều bean nữa ... -->
</beans>

Spring Boot là một tầng bên trên Spring Framework giúp giảm đáng kể lượng cấu hình. Nó sử dụng các giá trị mặc định hợp lýcấu hình tự động (auto-configuration) để giúp bạn bắt đầu với gần như không cần thiết lập gì. Cùng cấu hình cơ sở dữ liệu ở trên trong Spring Boot chỉ cần thế này:

# application.properties — đây là TẤT CẢ những gì bạn cần
spring.datasource.url=jdbc:mariadb://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password

Spring Boot nhìn vào những thư viện nào có trên classpath của bạn và tự động cấu hình chúng. Nếu nó thấy driver MariaDB, nó tự động cấu hình DataSource. Nếu nó thấy Spring MVC, nó tự động cấu hình web server. Đây chính là sự kỳ diệu của auto-configuration.

Một phép so sánh

Hãy nghĩ theo cách này:

  • Spring Framework giống như mua tất cả các bộ phận để lắp ráp một chiếc ô tô — động cơ, bánh xe, hộp số, ghế ngồi — và tự lắp ráp chúng.
  • Spring Boot giống như mua một chiếc ô tô đã được lắp ráp hoàn chỉnh với cấu hình tiêu chuẩn. Bạn vẫn có thể tùy chỉnh, nhưng nó hoạt động ngay khi xuất xưởng.

Trong suốt series này, khi chúng ta nói “Spring Boot,” chúng ta đang sử dụng Spring Boot trên nền Spring Framework, Spring MVC, Spring Data JPA, và Spring Security. Spring Boot gắn kết tất cả chúng lại với nhau.


Spring Boot giải quyết vấn đề gì — Convention over Configuration

Triết lý

Spring Boot tuân theo một nguyên tắc gọi là Convention over Configuration (Quy ước trên Cấu hình). Điều này có nghĩa:

Nếu có một giá trị mặc định hợp lý, hãy sử dụng nó. Chỉ yêu cầu lập trình viên cấu hình những thứ riêng biệt cho ứng dụng của họ.

Đây là các vấn đề chính mà Spring Boot giải quyết:

Vấn đề 1: Quản lý dependency hỗn loạn

Trong một dự án Spring truyền thống, bạn phải tự tìm và quản lý hàng chục phiên bản thư viện tương thích. Một phiên bản sai có thể phá vỡ mọi thứ.

Giải pháp của Spring Boot: Starter dependencies.

Thay vì thêm 10 thư viện riêng lẻ cho lập trình web, bạn chỉ cần thêm một starter:

<!-- Dependency duy nhất này kéo về mọi thứ bạn cần cho ứng dụng web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Starter duy nhất này bao gồm Spring MVC, Jackson (cho JSON), server Tomcat nhúng, validation, và logging — tất cả với các phiên bản tương thích đã được kiểm thử cùng nhau.

Vấn đề 2: Quá tải cấu hình XML

Như bạn đã thấy ở phần trước, Spring truyền thống yêu cầu hàng đống cấu hình XML.

Giải pháp của Spring Boot: Auto-configuration và annotation.

Spring Boot quét dự án của bạn, phát hiện những thư viện nào có sẵn, và cấu hình chúng tự động. Bạn chỉ cần cung cấp thông tin mà Spring Boot không thể đoán được (như URL cơ sở dữ liệu).

Vấn đề 3: Thiết lập và triển khai Server

Ứng dụng web Java truyền thống cần được đóng gói thành file WAR và triển khai lên một application server bên ngoài (Tomcat, JBoss, WebLogic). Việc thiết lập và quản lý các server này bản thân nó đã là một công việc.

Giải pháp của Spring Boot: Server nhúng (Embedded server).

Spring Boot bao gồm một server Tomcat nhúng (hoặc Jetty, hoặc Undertow) bên trong ứng dụng của bạn. Bạn chỉ cần chạy ứng dụng như bất kỳ chương trình Java bình thường nào:

java -jar myapp.jar

Không cần cài đặt server bên ngoài. Không cần triển khai file WAR. Ứng dụng của bạn chính LÀ server.

Vấn đề 4: Cấu hình cho từng môi trường

Các môi trường khác nhau (development, testing, production) cần các thiết lập khác nhau (URL cơ sở dữ liệu, API key, mức độ ghi log).

Giải pháp của Spring Boot: Profile và cấu hình ngoại vi.

# application-dev.properties
spring.datasource.url=jdbc:mariadb://localhost:3306/mydb_dev
logging.level.root=DEBUG

# application-prod.properties
spring.datasource.url=jdbc:mariadb://prod-server:3306/mydb_prod
logging.level.root=WARN

Bạn kích hoạt một profile chỉ với một cờ đơn giản:

java -jar myapp.jar --spring.profiles.active=prod

Thiết lập môi trường phát triển

Trước khi tạo dự án đầu tiên, hãy đảm bảo môi trường phát triển của bạn đã sẵn sàng. Bạn cần ba thứ: JDK, công cụ build, và IDE.

JDK (Java Development Kit)

Bạn nên đã cài Java rồi vì bạn đã biết Java. Đối với Spring Boot 3.x (phiên bản chúng ta sử dụng), bạn cần Java 17 trở lên.

Kiểm tra phiên bản Java của bạn:

java -version

Bạn sẽ thấy kết quả tương tự:

openjdk version "17.0.x" 2024-xx-xx

Nếu bạn cần cài đặt hoặc nâng cấp, tải về từ Adoptium (khuyến nghị) hoặc Oracle JDK.

Công cụ Build: Maven

Spring Boot hỗ trợ cả MavenGradle. Trong series này, chúng ta sẽ sử dụng Maven vì nó được sử dụng rộng rãi hơn trong hệ sinh thái Spring và có cấu hình XML đơn giản.

Nếu bạn đã sử dụng Java trước đây, bạn có thể đã cài Maven. Kiểm tra bằng:

mvn -version

Nếu chưa cài, tải về từ maven.apache.org và thêm vào biến PATH của hệ thống.

Lưu ý: Nếu bạn quen thuộc hơn với Gradle, mọi thứ trong series này đều có thể thực hiện với Gradle. Spring Initializr (phần tiếp theo) tạo được cả dự án Maven và Gradle.

IDE: IntelliJ IDEA

Mặc dù bạn có thể sử dụng bất kỳ IDE hoặc trình soạn thảo nào, IntelliJ IDEA là IDE tốt nhất cho lập trình Spring Boot. Phiên bản Community miễn phí và đủ dùng cho series này, nhưng phiên bản Ultimate có thêm các tính năng dành riêng cho Spring.

Các lựa chọn khác:

  • Visual Studio Code với Spring Boot Extension Pack
  • Eclipse / Spring Tool Suite (STS)

Với IntelliJ IDEA, hãy cài đặt các plugin hữu ích sau (hầu hết đã được bao gồm mặc định):

  • Spring Boot (chỉ Ultimate)
  • Maven Helper
  • Lombok (chúng ta sẽ sử dụng sau trong series)

Tùy chọn nhưng khuyến nghị: Postman hoặc HTTPie

Vì chúng ta đang xây dựng REST API, bạn cần một công cụ để kiểm thử chúng. Trình duyệt chỉ có thể gửi request GET dễ dàng. Đối với POST, PUT, và DELETE, bạn cần một công cụ:

  • Postman — Công cụ giao diện đồ họa để kiểm thử API. Tải về từ postman.com.
  • curl — Công cụ dòng lệnh (đã cài sẵn trên Mac/Linux).
  • HTTPie — Giải pháp dòng lệnh thân thiện hơn curl.

Chúng ta sẽ sử dụng Postman trong các ví dụ, nhưng bất kỳ công cụ nào kể trên đều hoạt động được.


Tạo dự án Spring Boot đầu tiên với Spring Initializr

Bây giờ đến phần thú vị — hãy tạo dự án Spring Boot đầu tiên của bạn.

Spring Initializr là gì?

Spring Initializr là một công cụ web (đồng thời cũng được tích hợp sẵn trong IntelliJ IDEA) tạo ra khung dự án Spring Boot cho bạn. Thay vì tạo tất cả các file và thư mục thủ công, bạn điền vào một biểu mẫu, nhấn nút, và tải về một dự án sẵn sàng sử dụng.

Sử dụng giao diện Web

Truy cập start.spring.io và điền vào biểu mẫu:

TrườngGiá trịGiải thích
ProjectMavenCông cụ build của chúng ta
LanguageJavaNgôn ngữ lập trình
Spring Boot3.3.x (bản ổn định mới nhất)Phiên bản Spring Boot
Groupcom.exampleTên miền tổ chức viết ngược (giống Java package)
Artifactblog-apiTên dự án
Nameblog-apiTên hiển thị
DescriptionBlog REST API with Spring Boot and MariaDBMô tả ngắn
Package namecom.example.blogapiPackage Java gốc
PackagingJarChúng ta muốn một JAR thực thi (không phải WAR)
Java17Phiên bản Java

Thêm Dependencies

Nhấn “Add Dependencies” và thêm:

  • Spring Web — Cung cấp Spring MVC để xây dựng REST API và bao gồm server Tomcat nhúng.

Hiện tại, đó là tất cả những gì chúng ta cần. Chúng ta sẽ thêm nhiều dependency khác (MariaDB, JPA, Security, v.v.) trong các bài giảng sau.

Nhấn “Generate” để tải về file ZIP. Giải nén nó vào thư mục workspace mà bạn thích.

Sử dụng IntelliJ IDEA (Phương pháp thay thế)

Nếu bạn đang sử dụng IntelliJ IDEA Ultimate, bạn có thể tạo dự án trực tiếp từ IDE:

  1. Vào File → New → Project
  2. Chọn Spring Initializr ở bên trái
  3. Điền các trường giống như trên
  4. Chọn dependency Spring Web
  5. Nhấn Create

IntelliJ sẽ tạo dự án và mở nó tự động.


Tìm hiểu cấu trúc dự án

Mở dự án đã tạo trong IDE. Bạn sẽ thấy cấu trúc này:

blog-api/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           └── blogapi/
│   │   │               └── BlogApiApplication.java
│   │   └── resources/
│   │       ├── static/
│   │       ├── templates/
│   │       └── application.properties
│   └── test/
│       └── java/
│           └── com/
│               └── example/
│                   └── blogapi/
│                       └── BlogApiApplicationTests.java
├── pom.xml
├── mvnw
├── mvnw.cmd
└── .gitignore

Hãy xem xét từng file và thư mục quan trọng:

pom.xml — File cấu hình dự án

Đây là file cấu hình của Maven. Nó định nghĩa các dependency, plugin, và thiết lập build của dự án.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <!-- Đây là parent POM — cung cấp cấu hình mặc định
         và phiên bản dependency cho các dự án Spring Boot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.0</version>
        <relativePath/>
    </parent>
    
    <!-- Tọa độ dự án — xác định duy nhất dự án của bạn -->
    <groupId>com.example</groupId>
    <artifactId>blog-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>blog-api</name>
    <description>Blog REST API with Spring Boot and MariaDB</description>
    
    <!-- Phiên bản Java -->
    <properties>
        <java.version>17</java.version>
    </properties>
    
    <dependencies>
        <!-- Spring Web Starter: bao gồm Spring MVC, Tomcat nhúng, 
             Jackson (thư viện JSON), và nhiều hơn nữa -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Lưu ý: không chỉ định phiên bản! Parent POM quản lý phiên bản 
             cho tất cả Spring Boot starter, nên chúng luôn tương thích -->
        
        <!-- Spring Boot Test Starter: bao gồm JUnit 5, Mockito, 
             MockMvc, và các công cụ kiểm thử khác -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <!-- Plugin này đóng gói ứng dụng thành file JAR thực thi
                 với server nhúng -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Các điểm quan trọng:

  • spring-boot-starter-parent quản lý tất cả phiên bản dependency cho bạn. Bạn không cần chỉ định phiên bản cho các Spring Boot starter.
  • spring-boot-starter-web là một dependency duy nhất kéo về mọi thứ cần thiết cho lập trình web.
  • spring-boot-maven-plugin cho phép bạn chạy ứng dụng bằng mvn spring-boot:run và đóng gói thành file JAR thực thi.

BlogApiApplication.java — Điểm khởi đầu

package com.example.blogapi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BlogApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(BlogApiApplication.class, args);
    }
}

Đây là trái tim của ứng dụng. Hãy tìm hiểu từng phần:

  • @SpringBootApplication — Annotation duy nhất này thực ra là phím tắt cho ba annotation kết hợp:

@SpringBootConfiguration — Đánh dấu class này là class cấu hình (giống @Configuration) – @EnableAutoConfiguration — Yêu cầu Spring Boot tự động cấu hình các bean dựa trên các thư viện trên classpath – @ComponentScan — Yêu cầu Spring quét package hiện tại và tất cả sub-package để tìm các component (@Controller, @Service, @Repository, v.v.)

  • SpringApplication.run(...) — Phương thức này khởi động toàn bộ ứng dụng. Nó tạo Spring Application Context, thực hiện auto-configuration, khởi động web server nhúng, và bắt đầu lắng nghe các HTTP request.

Đây là phương thức biến chương trình Java bình thường thành một web server. Chỉ với một dòng, bạn đi từ phương thức main() đến một ứng dụng web hoàn chỉnh đang chạy.

application.properties — File cấu hình

# File này hiện đang trống.
# Bạn sẽ thêm cấu hình ở đây khi ứng dụng phát triển.
# Ví dụ:
# server.port=8080
# spring.datasource.url=jdbc:mariadb://localhost:3306/mydb

Đây là nơi bạn cấu hình hành vi của ứng dụng. Spring Boot đọc file này khi khởi động và áp dụng các thiết lập. Chúng ta sẽ bổ sung nó trong các bài giảng sau khi thêm cơ sở dữ liệu, bảo mật, và các tính năng khác.

Các thư mục khác

  • src/main/resources/static/ — Cho các file tĩnh (HTML, CSS, JavaScript, hình ảnh). Chúng ta sẽ không sử dụng vì đang xây dựng REST API, không phải website truyền thống.
  • src/main/resources/templates/ — Cho các template HTML phía server (Thymeleaf, FreeMarker). Chúng ta cũng sẽ không sử dụng.
  • src/test/ — Cho các class kiểm thử. Chúng ta sẽ viết test trong Bài 15.
  • mvnwmvnw.cmd — Maven Wrapper. Các script này cho phép bất kỳ ai build dự án mà không cần cài Maven toàn cục. Sử dụng ./mvnw (Mac/Linux) hoặc mvnw.cmd (Windows) thay vì mvn.

Chạy ứng dụng & Server nhúng (Embedded Server)

Khởi động ứng dụng

Bạn có hai cách để chạy ứng dụng Spring Boot:

Cách 1: Từ IDE

Trong IntelliJ IDEA, mở BlogApiApplication.java và nhấn nút “Run” màu xanh lá bên cạnh phương thức main. Đây là cách thuận tiện nhất trong quá trình phát triển.

Cách 2: Từ Terminal

Di chuyển đến thư mục dự án và chạy:

# Sử dụng Maven Wrapper (khuyến nghị — không cần cài Maven)
./mvnw spring-boot:run

# Hoặc nếu bạn đã cài Maven toàn cục
mvn spring-boot:run

Điều gì xảy ra khi bạn chạy

Khi bạn thực thi ứng dụng, bạn sẽ thấy output như thế này trong console:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.0)

2024-01-15T10:30:00.123  INFO --- Starting BlogApiApplication
2024-01-15T10:30:01.456  INFO --- Tomcat initialized with port 8080 (http)
2024-01-15T10:30:01.789  INFO --- Starting service [Tomcat]
2024-01-15T10:30:01.890  INFO --- Starting Servlet engine: [Apache Tomcat/10.1.x]
2024-01-15T10:30:02.345  INFO --- Tomcat started on port 8080 (http)
2024-01-15T10:30:02.456  INFO --- Started BlogApiApplication in 2.5 seconds

Hãy đọc output này:

  1. Banner Spring Boot xuất hiện (nghệ thuật ASCII).
  2. Spring Boot bắt đầu khởi tạo — quét các component, chạy auto-configuration.
  3. Tomcat được khởi tạo trên cổng 8080. Đây là web server nhúng. Bạn không cài Tomcat — nó đi kèm với spring-boot-starter-web.
  4. Ứng dụng đã sẵn sàng và đang lắng nghe HTTP request tại http://localhost:8080.

Kiểm thử

Mở trình duyệt và truy cập http://localhost:8080. Bạn sẽ thấy trang “Whitelabel Error Page” với trạng thái 404:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

There was an unexpected error (type=Not Found, status=404).

Đây thực ra là một dấu hiệu tốt! Nó có nghĩa là server đang chạy và phản hồi các request. Lỗi 404 chỉ có nghĩa là chúng ta chưa định nghĩa endpoint nào. Chúng ta sẽ sửa điều đó trong phần tiếp theo.

Thay đổi cổng

Mặc định, Spring Boot chạy trên cổng 8080. Nếu cổng đó đã được sử dụng, bạn có thể thay đổi trong application.properties:

server.port=9090

Bây giờ ứng dụng sẽ khởi động tại http://localhost:9090.

Giải thích về Server nhúng

Trong lập trình web Java truyền thống, bạn sẽ phải:

  1. Cài đặt application server (Tomcat, JBoss, WildFly) trên máy
  2. Đóng gói ứng dụng thành file WAR
  3. Triển khai file WAR lên application server
  4. Khởi động application server

Với cách tiếp cận server nhúng của Spring Boot:

  1. Ứng dụng bao gồm cả server
  2. Bạn chạy ứng dụng trực tiếp: java -jar blog-api.jar
  3. Xong

Đây là một thay đổi căn bản. Ứng dụng của bạn là tự chứa và di động. Bạn có thể chạy nó ở bất kỳ đâu có Java — trên laptop, máy ảo, Docker container, hay nền tảng cloud.


Thực hành: Endpoint REST “Hello World”

Đã đến lúc viết code Spring Boot thực sự đầu tiên. Chúng ta sẽ tạo một REST endpoint trả về response JSON.

Bước 1: Tạo class Controller

Trong IDE, tạo một class Java mới trong package com.example.blogapi:

File: src/main/java/com/example/blogapi/HelloController.java

package com.example.blogapi;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

// Annotation này cho Spring biết class này xử lý HTTP request
// và giá trị trả về của mọi phương thức sẽ được ghi trực tiếp
// vào phần thân HTTP response (mặc định là JSON).
@RestController
public class HelloController {

    // Annotation này ánh xạ HTTP GET request cho "/hello" đến phương thức này.
    // Khi ai đó truy cập http://localhost:8080/hello, phương thức này chạy.
    @GetMapping("/hello")
    public Map<String, String> hello() {
        // Chúng ta trả về một Map, và Spring Boot tự động chuyển nó thành JSON.
        // Không cần chuyển đổi JSON thủ công!
        Map<String, String> response = new HashMap<>();
        response.put("message", "Hello, World!");
        response.put("framework", "Spring Boot");
        return response;
    }

    // Endpoint này minh họa cách nhận query parameter.
    // Ví dụ: http://localhost:8080/greet?name=Alice
    //
    // @RequestParam yêu cầu Spring trích xuất tham số "name" từ URL.
    // defaultValue đảm bảo ứng dụng không bị crash nếu "name" không được cung cấp.
    @GetMapping("/greet")
    public Map<String, Object> greet(
            @RequestParam(defaultValue = "World") String name) {
        
        Map<String, Object> response = new HashMap<>();
        response.put("message", "Hello, " + name + "!");
        response.put("timestamp", LocalDateTime.now().toString());
        return response;
    }

    // Endpoint này trả về thông tin trạng thái server.
    // Đây là pattern phổ biến — có một endpoint /health hoặc /status
    // để các công cụ giám sát có thể kiểm tra.
    @GetMapping("/status")
    public Map<String, Object> status() {
        Map<String, Object> response = new HashMap<>();
        response.put("status", "UP");
        response.put("application", "Blog API");
        response.put("javaVersion", System.getProperty("java.version"));
        response.put("timestamp", LocalDateTime.now().toString());
        return response;
    }
}

Bước 2: Hiểu code

Hãy đi qua từng annotation và khái niệm:

@RestController

Annotation này kết hợp hai thứ:

  • @Controller — Đánh dấu class là một Spring MVC controller (component xử lý HTTP request)
  • @ResponseBody — Yêu cầu Spring ghi giá trị trả về trực tiếp vào phần thân HTTP response

Nếu không có @ResponseBody, Spring sẽ cố tìm một template HTML để render. Với @RestController, nó biết phải chuyển đổi giá trị trả về thành JSON và gửi lại.

@GetMapping("/hello")

Annotation này ánh xạ HTTP GET request đến đường dẫn URL /hello vào phương thức được đánh dấu. Có các annotation tương tự cho các phương thức HTTP khác:

  • @PostMapping — cho POST request
  • @PutMapping — cho PUT request
  • @DeleteMapping — cho DELETE request
  • @PatchMapping — cho PATCH request

Chúng ta sẽ sử dụng tất cả trong Bài 3 khi xây dựng CRUD API hoàn chỉnh.

@RequestParam

Annotation này gắn kết một query parameter từ URL vào tham số phương thức. Khi ai đó truy cập /greet?name=Alice, Spring trích xuất giá trị "Alice" và truyền vào tham số name.

defaultValue = "World" có nghĩa là nếu ai đó truy cập /greet mà không có tham số name, giá trị sẽ mặc định là "World".

Chuyển đổi giá trị trả về → JSON

Lưu ý chúng ta đang trả về MapMap. Spring Boot sử dụng thư viện Jackson (đi kèm trong spring-boot-starter-web) để tự động chuyển đổi đối tượng Java thành JSON. Đây được gọi là serialization (tuần tự hóa). Sau trong series, chúng ta sẽ trả về các class Java tùy chỉnh thay vì Map, và Jackson sẽ serialize chúng theo cách tương tự.

Bước 3: Khởi động lại và kiểm thử

Dừng ứng dụng đang chạy (nhấn nút “Stop” màu đỏ trong IDE, hoặc nhấn Ctrl+C trong terminal) và khởi động lại. Spring Boot cần khởi động lại để nhận controller mới.

Mẹo: Trong quá trình phát triển sau này, bạn có thể thêm dependency spring-boot-devtools để tự động khởi động lại khi code thay đổi. Hiện tại, khởi động lại thủ công là đủ.

Bây giờ hãy kiểm thử từng endpoint:

Kiểm thử 1: Sử dụng trình duyệt

Mở http://localhost:8080/hello trong trình duyệt. Bạn sẽ thấy:

{
  "message": "Hello, World!",
  "framework": "Spring Boot"
}

Kiểm thử 2: Sử dụng curl (dòng lệnh)

# Kiểm thử endpoint /hello
curl http://localhost:8080/hello

# Kiểm thử endpoint /greet với tham số name
curl http://localhost:8080/greet?name=Alice

# Kiểm thử endpoint /greet không có tham số name (sử dụng giá trị mặc định)
curl http://localhost:8080/greet

# Kiểm thử endpoint /status
curl http://localhost:8080/status

Kiểm thử 3: Sử dụng Postman

  1. Mở Postman
  2. Tạo một request mới
  3. Đặt phương thức là GET
  4. Nhập http://localhost:8080/hello làm URL
  5. Nhấn Send
  6. Bạn sẽ thấy response JSON ở panel phía dưới

Bước 4: Thử nghiệm

Hãy thử các bài tập sau để củng cố kiến thức:

  1. Thêm endpoint mới: Tạo phương thức @GetMapping("/about") trả về thông tin về blog API (tên, phiên bản, tác giả).
  2. Nhiều tham số: Tạo @GetMapping("/add") nhận hai query parameter (ab) và trả về tổng của chúng. Ví dụ, /add?a=5&b=3 sẽ trả về {"result": 8}.
  3. Thay đổi cổng: Sửa application.properties để chạy server trên cổng 3000 và xác nhận nó hoạt động.

Đây là lời giải cho bài tập 2 để bạn kiểm tra:

@GetMapping("/add")
public Map<String, Object> add(
        @RequestParam int a,
        @RequestParam int b) {
    
    Map<String, Object> response = new HashMap<>();
    response.put("a", a);
    response.put("b", b);
    response.put("result", a + b);
    return response;
}

Lưu ý rằng Spring tự động chuyển đổi các chuỗi query parameter thành giá trị int. Nếu ai đó gửi giá trị không phải số, Spring sẽ tự động trả về lỗi 400 Bad Request.


Tổng kết

Trong bài giảng đầu tiên này, bạn đã đi qua rất nhiều kiến thức:

  • Web hoạt động như thế nào: Trình duyệt gửi HTTP request, server xử lý chúng và trả về HTTP response. Với tư cách là lập trình viên Spring Boot, bạn viết code xử lý phía server.
  • Kiến trúc client-server: Client xử lý giao diện, server xử lý dữ liệu và logic. Chúng giao tiếp qua HTTP, thường trao đổi JSON.
  • Tại sao sử dụng framework: Framework như Spring Boot loại bỏ code boilerplate để bạn tập trung vào logic riêng của ứng dụng.
  • Hệ sinh thái Spring: Spring Framework là động cơ, Spring Boot là chiếc xe sẵn sàng lái được xây dựng trên động cơ đó.
  • Convention over Configuration: Spring Boot cung cấp các giá trị mặc định thông minh — server nhúng, starter dependency, auto-configuration, và profile.
  • Thiết lập dự án: Spring Initializr tạo cấu trúc dự án hoàn chỉnh. pom.xml quản lý dependency, BlogApiApplication.java là điểm khởi đầu, và application.properties chứa cấu hình.
  • Endpoint REST đầu tiên: Sử dụng @RestController@GetMapping, bạn có thể tạo endpoint trả về JSON chỉ với vài dòng code.

Bài tiếp theo

Trong Bài 2, chúng ta sẽ đi sâu vào Dependency Injection — khái niệm quan trọng nhất trong hệ sinh thái Spring. Bạn sẽ học cách Spring quản lý các đối tượng (bean) cho bạn và tại sao điều này giúp code của bạn trở nên module hóa, dễ kiểm thử, và dễ bảo trì hơn. Đây là khái niệm mở khóa mọi thứ khác trong Spring Boot.


Tham chiếu nhanh

Khái niệmMô tả
HTTPGiao thức giao tiếp giữa client và server
REST APIAPI sử dụng các phương thức HTTP để thực hiện thao tác CRUD
Spring BootFramework có chính kiến, xây dựng trên Spring Framework để phát triển nhanh
@SpringBootApplicationAnnotation chính kích hoạt auto-configuration và quét component
@RestControllerĐánh dấu class là controller REST API
@GetMappingÁnh xạ đường dẫn URL vào phương thức cho HTTP GET request
@RequestParamGắn kết query parameter URL vào tham số phương thức
application.propertiesFile cấu hình chính
pom.xmlCấu hình dự án Maven và quản lý dependency
Tomcat nhúngWeb server tích hợp sẵn đi kèm với spring-boot-starter-web

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *