99 Ngày Java — Ngày 86: Maven
Một dự án Java thật có hàng chục thư viện, mỗi thư viện lại kéo theo thư viện khác. Maven giải quyết hai việc: mô tả dự án cùng phụ thuộc trong một file pom.xml, và chạy build theo một vòng đời cố định mà mọi dự án đều tuân theo. Hiểu bốn khái niệm tọa độ, scope, lifecycle và BOM là đủ để đọc gần như mọi POM.

1. POM và tọa độ
Mỗi artifact được định danh bằng ba giá trị: groupId, artifactId và version. Maven dùng bộ ba này để tải thư viện từ Maven Central về kho cục bộ ~/.m2. Dự án theo cấu trúc quy ước: mã nguồn ở src/main/java, test ở src/test/java, tài nguyên ở src/main/resources. Thuộc tính maven.compiler.release bảo đảm bytecode và API đều đúng phiên bản Java đích.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>vn.motdev</groupId>
<artifactId>orders</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>2. Dependency scope
Scope quyết định thư viện có mặt ở classpath nào. compile là mặc định: có ở mọi nơi và được đóng gói. provided có lúc biên dịch nhưng môi trường chạy tự cung cấp, ví dụ Servlet API. runtime không cần lúc biên dịch, ví dụ driver JDBC. test chỉ có khi biên dịch và chạy test. Chọn sai scope thường không gây lỗi ngay mà làm gói phình to hoặc thiếu lớp lúc chạy.
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.4</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.3</version>
<scope>test</scope>
</dependency>
</dependencies>3. Phụ thuộc bắc cầu và xung đột phiên bản
Khai báo một thư viện là kéo theo cả cây phụ thuộc của nó. Khi hai nhánh cần hai phiên bản khác nhau của cùng một artifact, Maven chọn phiên bản gần gốc cây nhất, không phải phiên bản mới nhất. Kết quả có thể là một phiên bản cũ thiếu phương thức mà thư viện khác cần, và lỗi NoSuchMethodError chỉ xuất hiện lúc chạy. Lệnh dependency:tree cho thấy chính xác phiên bản nào thắng và vì sao.
./mvnw dependency:tree -Dincludes=com.fasterxml.jackson.core
./mvnw dependency:analyze # declared but unused, used but undeclared4. Lifecycle và BOM
Vòng đời mặc định gồm các phase theo thứ tự: validate, compile, test, package, verify, install, deploy. Gọi một phase là chạy mọi phase đứng trước nó. Plugin gắn goal vào phase; đó là nơi công việc thật sự diễn ra. BOM là một POM chỉ chứa phiên bản: import nó vào dependencyManagement thì các dependency cùng họ không cần ghi version nữa và luôn khớp nhau.
Dùng Maven Wrapper (./mvnw) để cả đội và CI build cùng một phiên bản Maven.
mvn verify là lệnh kiểm tra đầy đủ trước khi đẩy code; install chỉ cần khi dự án khác trên máy dùng artifact này.
Không ghi version rải rác: gom vào BOM hoặc properties.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.11.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>Maven đổi sự linh hoạt lấy tính dự đoán được: ai đọc POM cũng biết build chạy thế nào. Ngày 87 xem lựa chọn còn lại, Gradle.
Tài liệu đối chiếu
https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
Site Admin
Engineer and writer. Building things with TypeScript and distributed systems.
Bình luận (0)
Bạn cần đăng nhập bằng Google để bình luận.
Hãy là người bình luận đầu tiên.


