Advanced Java là gì?
Xem thửMột người học cài JDK cũ từ nhiều năm trước, mở dự án đầu tiên của khóa và thấy bước build báo lỗi. Hai giờ sau họ vẫn đang tìm lỗi trong mã, trong khi nguyên nhân chỉ là phiên bản công cụ trên máy không đáp ứng yêu cầu của bài. Họ nghĩ mình học kém; thật ra họ chưa từng kiểm tra môi trường.
Bài mở đầu này làm hai việc. Thứ nhất, nó nói rõ khóa học dạy gì và theo thứ tự nào, để mỗi chương sau nằm đúng chỗ trong một bức tranh. Thứ hai, nó cho bạn một chương trình nhỏ để kiểm tra máy mình trước khi bắt đầu, vì một môi trường không đúng làm mất nhiều thời gian hơn bất kỳ lỗi nào trong mã.
Sau bài này, bạn có thể
- Mô tả Advanced Java gồm những gì và phân biệt nó với Core Java.
- Vẽ đường đi của một yêu cầu qua các tầng của ứng dụng backend và đặt mỗi chương của khóa vào đúng tầng.
- Chạy chương trình kiểm tra môi trường và đọc kết quả
PASS,WARN,FAIL. - Biết cách dùng kho mã của khóa: mỗi lab nằm trong một thư mục, có README, chạy và kiểm thử độc lập.
Advanced Java là gì
Core Java là ngôn ngữ và thư viện chuẩn: cú pháp, hướng đối tượng, collections, xử lý ngoại lệ, đa luồng, vào ra. Advanced Java là mọi thứ bạn cần thêm để viết ứng dụng doanh nghiệp, web và backend: truy cập cơ sở dữ liệu (JDBC, Hibernate), tầng web (Servlet, JSP), khung ứng dụng (Spring, Spring Boot), bảo mật, kiến trúc nhiều dịch vụ và kiểm thử. Nó không phải một ngôn ngữ khác: đó là các lớp được xây trên Java mà bạn nếu không dùng thì phải tự viết (bài sau đo chính điều đó).
Điều kiện tiên quyết: bạn cần nắm Core Java (cú pháp, OOP, collections, ngoại lệ, đa luồng cơ bản) trước khi học khóa này.
Một yêu cầu đi qua các tầng
Cách đọc hình: yêu cầu đi từ trình duyệt qua bộ lọc bảo mật, tầng web, tầng service, tầng dữ liệu rồi tới cơ sở dữ liệu. Dòng chữ nhỏ trong mỗi ô cho biết chương nào của khóa học nói về tầng đó. Ghi chú dưới hình: chương 13 và 14 nói về điều thay đổi khi chia ngăn xếp này thành nhiều dịch vụ, chương 15 về kiểm thử.
Đang tải Animation…
Lộ trình khóa học
Khóa học đi từ dưới lên: từ cách Java nói chuyện với cơ sở dữ liệu, qua tầng web, ORM, tới hệ sinh thái Spring, rồi tới kiến trúc nhiều dịch vụ và kiểm thử. Mỗi chương có bài học kèm các lab chạy được trong kho mã của khóa; chương cuối là một dự án tổng hợp.
| Chương | Nội dung | Bạn sẽ làm và đo gì |
|---|---|---|
| 1. Giới thiệu | Bức tranh tổng thể, môi trường, socket so với servlet | Kiểm tra máy; so máy chủ viết tay với servlet container |
| 2. JDBC | Kết nối, truy vấn, giao dịch, pool | Kết nối tới MySQL, PreparedStatement, giao dịch, batch |
| 3. Servlet | Vòng đời, request, response, session, filter | Tomcat nhúng, ánh xạ URL, filter |
| 4. JSP | Trang web động, MVC với servlet | Trang JSP, EL, tag |
| 5. Hibernate | ORM, ánh xạ, HQL, hiệu năng | Cấu hình, N+1, khóa, bộ nhớ đệm |
| 6. Spring Core | IoC, DI, phạm vi bean | Container, cấu hình, vòng đời bean |
| 7. Spring MVC | DispatcherServlet, controller, validation | Luồng xử lý yêu cầu, lỗi |
| 8. Spring Boot | Tự cấu hình, REST, Actuator | Khởi động, cấu hình, quan sát |
| 9. Spring Data JPA | Repository, truy vấn, giao dịch | Phân trang, N+1, giao dịch |
| 10. Spring JDBC | JdbcTemplate | Batch, xử lý ngoại lệ |
| 11. Spring Security | Xác thực, phân quyền, JWT | Chuỗi bộ lọc, mã hóa mật khẩu, token |
| 12. Spring AOP | Proxy, advice | Proxy, thứ tự advice, giao dịch |
| 13. Spring Cloud | Discovery, Feign, Gateway, Config, Tracing | Đo cửa sổ lỗi, circuit breaker, vết |
| 14. Microservices | Kiến trúc, nhất quán dữ liệu, Docker | Cái giá của việc tách, saga, Docker Compose |
| 15. JUnit & Testing | JUnit, Mockito, kiểm thử Spring | Vòng đời, TDD, lát cắt, Testcontainers |
Mọi bài sau đều theo một khung: mở bằng một sự cố có thật, hình có phần "Cách đọc hình", số đo từ chính lab, bảng tra cứu khi gặp sự cố, bài thực hành có tiêu chí nghiệm thu và câu hỏi tự kiểm tra.
Chuẩn bị môi trường và dự án
Mọi thí nghiệm của bài chạy trong một dự án Maven nhỏ. Mục này nói rõ cần cài gì, khai báo thư viện nào và cấu hình ra sao; mã của từng lớp được trình bày ngay tại chỗ nó được dùng, kèm giải thích, nên bạn không cần mở nơi nào khác. Số liệu và thông báo lỗi trong bài được đo từ chính mã đó (JDK 17, Maven 3.9). Bản sao lưu cả dự án nằm ở kho mã của khóa học, thư mục 01-nhap-mon/01-kiem-tra-moi-truong, chỉ để bạn clone cho nhanh nếu không muốn tự gõ lại.
Yêu cầu môi trường
| Thành phần | Phiên bản đã kiểm chứng | Ghi chú |
|---|---|---|
| JDK | 17 trở lên | java -version |
| Maven | 3.9 trở lên | mvn -v |
| Cổng 3307 | tùy chọn | MySQL của các bài JDBC; WARN nếu chưa chạy |
| Docker | bất kỳ bản gần đây | không bắt buộc cho bài này; các bài JDBC, kiểm thử và microservices cần tới |
Cấu trúc
01-kiem-tra-moi-truong/
pom.xml Maven thuần, release 17, JUnit và AssertJ cho test
src/main/java/dev/canhdominich/intro/env/
EnvCheck.java sáu phép kiểm tra và mã thoát
Khai báo thư viện: pom.xml
pom.xml
Dự án Maven thuần, chỉ cần JUnit 6.0.3 và AssertJ 3.27.7 cho phần test; chương trình chính EnvCheck không dùng thư viện nào ngoài JDK. maven.compiler.release đặt là 17, mức tối thiểu của khóa học. Plugin exec-maven-plugin chạy EnvCheck bằng mvn -q compile exec:exec và maven-surefire-plugin 3.5.6 chạy test.
<?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>
<groupId>dev.canhdominich.javabackend</groupId>
<artifactId>intro-kiem-tra-moi-truong</artifactId>
<version>1.0.0</version>
<name>Nhap mon - Kiem tra moi truong</name>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.class>dev.canhdominich.intro.env.EnvCheck</main.class>
<main.arg>none</main.arg>
<junit.version>6.0.3</junit.version>
<assertj.version>3.27.7</assertj.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.6</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.6.3</version>
<configuration>
<executable>java</executable>
<arguments>
<argument>-classpath</argument>
<classpath/>
<argument>${main.class}</argument>
<argument>${main.arg}</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
</project>
Các thành phần của bài
Bài có một lab nhỏ, EnvCheck, để kiểm tra máy bạn. Nó không dùng thư viện nào ngoài JDK. Check ghi một kết quả và mức độ của nó; run() thực hiện sáu phép kiểm tra: phiên bản JDK (phải từ 17), bảng mã mặc định (UTF-8 tránh nhiều lỗi hiển thị tiếng Việt), bộ nhớ heap tối đa, Maven (chạy lệnh mvn -v), Docker (docker version) và cổng 3307 nơi MySQL của các bài JDBC sẽ chạy. FAIL nghĩa là bạn chưa thể làm bài; WARN nghĩa là một phần khóa học (Docker, MySQL) chưa dùng được nhưng vẫn học tiếp được các bài khác. Chương trình thoát với mã 1 nếu có FAIL, nên dùng được trong một script:
src/main/java/dev/canhdominich/intro/env/EnvCheck.java
package dev.canhdominich.intro.env;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class EnvCheck {
public enum Level {
PASS, WARN, FAIL
}
public record Check(Level level, String text) {
@Override
public String toString() {
return String.format("%-5s %s", level, text);
}
}
public static void main(String[] args) {
List<Check> checks = run();
checks.forEach(System.out::println);
System.exit(checks.stream().anyMatch(c -> c.level() == Level.FAIL) ? 1 : 0);
}
public static List<Check> run() {
List<Check> checks = new ArrayList<>();
int feature = Runtime.version().feature();
checks.add(new Check(feature >= 17 ? Level.PASS : Level.FAIL, "JDK " + Runtime.version() + " (" + System.getProperty("java.vendor") + "), the course needs 17 or newer"));
checks.add(new Check(Level.PASS, "default charset " + Charset.defaultCharset() + ", native encoding " + System.getProperty("native.encoding")));
checks.add(new Check(Runtime.getRuntime().maxMemory() >= 256L * 1024 * 1024 ? Level.PASS : Level.WARN,
"maximum heap " + Runtime.getRuntime().maxMemory() / (1024 * 1024) + " MB, processors " + Runtime.getRuntime().availableProcessors()));
String maven = firstLine("mvn", "-v");
checks.add(new Check(maven != null ? Level.PASS : Level.FAIL, maven != null ? maven : "Maven: the command mvn was not found on the PATH"));
String docker = firstLine("docker", "version", "--format", "{{.Server.Version}}");
checks.add(new Check(docker != null && !docker.isBlank() ? Level.PASS : Level.WARN,
docker != null && !docker.isBlank() ? "Docker engine " + docker : "Docker is not available: the MySQL and Testcontainers labs need it"));
boolean mysql = reachable("localhost", 3307);
checks.add(new Check(mysql ? Level.PASS : Level.WARN, mysql ? "something listens on localhost:3307 (the MySQL of the JDBC labs)" : "nothing listens on localhost:3307: start it with the docker-compose.yml of the JDBC labs"));
return checks;
}
static String firstLine(String... command) {
try {
Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
if (!process.waitFor(15, TimeUnit.SECONDS)) {
process.destroyForcibly();
return null;
}
if (process.exitValue() != 0) {
return null;
}
return new String(process.getInputStream().readAllBytes()).lines().findFirst().orElse("");
} catch (IOException | InterruptedException e) {
return null;
}
}
static boolean reachable(String host, int port) {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(host, port), 500);
return true;
} catch (IOException e) {
return false;
}
}
}
EnvCheckTest kiểm tra rằng máy bạn không có FAIL, và hai hàm phụ biết nói "không" đúng cách: một lệnh không tồn tại cho null, một cổng đóng cho false:
src/test/java/dev/canhdominich/intro/env/EnvCheckTest.java
package dev.canhdominich.intro.env;
import static org.assertj.core.api.Assertions.assertThat;
import dev.canhdominich.intro.env.EnvCheck.Check;
import dev.canhdominich.intro.env.EnvCheck.Level;
import java.util.List;
import org.junit.jupiter.api.Test;
class EnvCheckTest {
@Test
void thisMachineMeetsTheMinimumForTheCourse() {
List<Check> checks = EnvCheck.run();
assertThat(checks).noneMatch(check -> check.level() == Level.FAIL);
assertThat(checks).hasSize(6);
}
@Test
void aMissingCommandIsReportedAsNull() {
assertThat(EnvCheck.firstLine("this-command-does-not-exist")).isNull();
}
@Test
void aClosedPortIsNotReachable() {
assertThat(EnvCheck.reachable("localhost", 1)).isFalse();
}
}
Chạy bằng mvn -q compile exec:exec. Kết quả trên máy đã kiểm chứng của khóa (phiên bản của bạn có thể khác, miễn là không có FAIL):
PASS JDK 17.0.15+6-Ubuntu-0ubuntu120.04 (Ubuntu), the course needs 17 or newer
PASS default charset UTF-8, native encoding UTF-8
PASS maximum heap 3972 MB, processors 4
PASS Apache Maven 3.9.12 (848fbb4bf2d427b72bdb2471c22fced7ebd9a7a1)
PASS Docker engine 23.0.2
PASS something listens on localhost:3307 (the MySQL of the JDBC labs)
Cách đọc từng dòng:
| Dòng | Nghĩa | Nếu không đạt |
|---|---|---|
| JDK | Phiên bản Java đang chạy phải từ 17 | FAIL: cài JDK 17 trở lên, đặt JAVA_HOME |
| default charset | Bảng mã mặc định của JVM | Nếu không phải UTF-8, thêm -Dfile.encoding=UTF-8 khi gặp lỗi chữ |
| maximum heap | Bộ nhớ tối đa JVM được dùng | WARN nếu dưới 256 MB: một số lab cần nhiều hơn |
| Maven | Công cụ build, phải có trong PATH | FAIL: cài Maven 3.9 trở lên |
| Docker | Cần cho MySQL, Testcontainers và chương microservices | WARN: chỉ cần từ chương 2 |
| localhost:3307 | MySQL của các bài JDBC | WARN: khởi động bằng docker-compose.yml của các bài JDBC |
Cách học với khóa này
- Chạy trước khi đọc kết quả. Mỗi bài cho mã, cách chạy và kết quả mong đợi. Hãy chạy rồi so với kết quả, đừng chỉ đọc.
- Mỗi lab là một dự án Maven độc lập trong kho mã của khóa (README của kho). Vào thư mục lab, đọc
README.md(yêu cầu, cấu trúc, lệnh chạy, kết quả mong đợi, lỗi thường gặp), rồi chạy. - Làm bài thực hành. Mỗi bài có các bước đánh số với tiêu chí nghiệm thu; bước làm hỏng cố ý quan trọng nhất vì bạn thấy lỗi thật trông ra sao.
- Ghi lại điều bạn thấy. Con số trên máy bạn khác con số trong bài là bình thường; thứ tự và quan hệ giữa chúng mới là điều cố định.
Quyết định thiết kế và đánh đổi
| Quyết định | Lựa chọn | Đánh đổi |
|---|---|---|
| Học bằng lab chạy được | Mỗi bài một dự án nhỏ | Mất thời gian cài đặt lúc đầu, đổi lại hiểu thật |
| Xây từ dưới lên (JDBC trước Spring) | Hiểu cơ chế dưới framework | Lâu hơn học thẳng framework nhưng ít "phép màu" |
| Kiểm tra môi trường trước | Một lệnh, sáu kiểm tra | Thêm một bước nhưng tránh lỗi khó hiểu về sau |
| Docker cho cơ sở dữ liệu và kiểm thử | Môi trường lặp lại được | Cần cài Docker |
Playbook: gặp sự cố ở đầu khóa thì làm gì
| Triệu chứng | Kiểm tra đầu tiên | Nguyên nhân hay gặp | Cách xử lý |
|---|---|---|---|
| Bước build của lab báo lỗi biên dịch phiên bản | EnvCheck dòng JDK | JDK cũ hơn 17 | Cài JDK 17 trở lên |
mvn không chạy | EnvCheck dòng Maven | Maven chưa có trong PATH | Cài và mở lại terminal |
| Chữ tiếng Việt hiện sai | Dòng default charset | Bảng mã mặc định không phải UTF-8 | Đặt -Dfile.encoding=UTF-8 |
| Lab MySQL không kết nối | Dòng localhost:3307 | MySQL chưa chạy | Khởi động bằng Docker Compose của bài JDBC |
| Lab Docker báo lỗi | Dòng Docker | Docker chưa cài hoặc chưa chạy | Cài Docker và thử docker version |
| Con số khác bài | Máy và tải | Cấu hình máy khác | So thứ tự và quan hệ thay vì giá trị tuyệt đối |
Lỗi thường gặp khi chạy lab
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
FAIL JDK ... the course needs 17 or newer | Đang dùng JDK cũ hơn 17 | Cài JDK 17 trở lên và đặt JAVA_HOME; chạy lại java -version |
FAIL Maven: the command mvn was not found | Maven chưa có trong PATH | Cài Maven 3.9 và mở lại terminal; kiểm tra mvn -v |
WARN Docker is not available | Docker chưa cài hoặc chưa chạy | Cài Docker và chạy docker version; chỉ cần từ chương JDBC trở đi |
Bài thực hành
Làm trong dự án của bài (thư mục 01-nhap-mon/01-kiem-tra-moi-truong). Sau mỗi bước, so kết quả với tiêu chí nghiệm thu.
- Chạy kiểm tra.
mvn -q compile exec:exec. Tiêu chí: không có dòngFAIL; ghi lại dòngWARN(nếu có) và quyết định khi nào xử lý. - Chạy bộ test.
mvn test. Tiêu chí:Tests run: 3, Failures: 0, Errors: 0. - Giả lập môi trường sai. Đổi điều kiện
feature >= 17thànhfeature >= 99trongEnvCheck.run. Tiêu chí: dòng JDK thànhFAIL, chương trình thoát với mã 1 (echo $?ngay sau khi chạy), vàEnvCheckTestđỏ; đổi lại sau khi xong. - Thêm một kiểm tra. Thêm một kiểm tra thứ bảy cho một công cụ bạn cần (ví dụ
git --version) bằngfirstLine. Tiêu chí: thêm một dòngPASShoặcWARN; cập nhậthasSizetrong test. - Đọc kho mã. Mở README của kho mã và của một lab JDBC bất kỳ. Tiêu chí: tìm được mục yêu cầu môi trường, lệnh chạy và kết quả mong đợi của lab đó.
Hiểu lầm và giới hạn
- "Advanced Java là một ngôn ngữ khác." Nó là các lớp thư viện và khung xây trên cùng ngôn ngữ Java.
- "Học thẳng Spring Boot là đủ." Spring che nhiều cơ chế; khóa này đi từ dưới lên để bạn biết thứ nó che.
- "Môi trường đúng là chuyện nhỏ." Môi trường sai làm mất thời gian và dễ tưởng nhầm là lỗi của mình.
- Kết quả
EnvCheckở trên thuộc về máy đã kiểm chứng (JDK 17.0.15, Maven 3.9.12, Docker 23.0.2, 4 lõi); bài chưa kiểm tra phiên bản Git, IDE, mạng, quyền ghi đĩa hay dung lượng đĩa trống (một số lab cần vài trăm MB).
Tự kiểm tra
- Advanced Java khác Core Java ở điểm nào, và có phải là một ngôn ngữ khác không?
- Kể tên các tầng của một ứng dụng backend theo thứ tự yêu cầu đi qua.
FAILvàWARNtrongEnvCheckkhác nhau thế nào?- Vì sao khóa học đi từ JDBC lên Spring chứ không bắt đầu bằng Spring Boot?
- Khi một lab cho con số khác trong bài, bạn so sánh điều gì?
Gợi ý đáp án. (1) Advanced Java là các thư viện và khung cho ứng dụng doanh nghiệp xây trên cùng ngôn ngữ Java. (2) Bộ lọc bảo mật, tầng web, tầng service, tầng dữ liệu, cơ sở dữ liệu. (3) FAIL chặn bạn làm bài, WARN chỉ chặn một phần (như Docker hoặc MySQL). (4) Để hiểu cơ chế mà framework che đi. (5) Thứ tự và quan hệ giữa các con số, không phải giá trị tuyệt đối.
Điều cần nhớ
- Advanced Java là các tầng xây trên Java: dữ liệu, web, khung ứng dụng, bảo mật, nhiều dịch vụ, kiểm thử.
- Một yêu cầu đi qua bảo mật, web, service, dữ liệu; mỗi chương đào sâu một tầng.
- Kiểm tra môi trường trước: JDK 17 trở lên, Maven, UTF-8; Docker và MySQL cho các chương sau.
- Chạy lab, so kết quả, làm bước làm hỏng cố ý.
Đọc thêm
Bài tiếp theo đặt Core Java cạnh Advanced Java bằng một phép đo: cùng một ứng dụng web viết bằng socket thuần và bằng servlet.