CodeGym /Các khóa học /JAVA 25 SELF /module-info.java: cú pháp, tạo mô-đun

module-info.java: cú pháp, tạo mô-đun

JAVA 25 SELF
Mức độ , Bài học
Có sẵn

1. module-info.java là gì và nó ở đâu?

Một mô-đun trong Java luôn bắt đầu với tệp module-info.java. Đây giống như “hộ chiếu” của mô-đun, nơi bạn khai báo tên, các gói được export và các phụ thuộc vào mô-đun khác.

Tìm ở đâu?
Tệp module-info.java phải nằm ở thư mục gốc của mã nguồn mô-đun của bạn. Ví dụ, nếu cấu trúc dự án như sau:

project-root/
└── src/
    └── my.module.name/
        ├── module-info.java
        └── com/
            └── example/
                └── api/
                    └── MyClass.java

Ở đây my.module.name là tên mô-đun (quy tắc đặt tên sẽ nói sau).

Không có tệp này thì mô-đun không được coi là mô-đun!
Nếu thiếu nó — đó chỉ là một dự án Java kiểu cũ, dù có rất nhiều thư mục và lớp.

2. Cú pháp module-info.java: các thành phần chính

Hãy xem ngay ví dụ đơn giản nhất — không đáng sợ đâu, thật đấy!

module my.module.name {
    exports com.example.api;
    requires java.sql;
}

Phân tích từng phần:

module my.module.name { ... }
Đây là khai báo mô-đun với tên my.module.name. Tên mô-đun là định danh duy nhất, thường trùng với gói gốc (ví dụ, com.example.app).
Một điều thú vị: nếu bạn đặt tên mô-đun là java.base, trình biên dịch sẽ không vui đâu. Đừng cố gắng thay thế các mô-đun chuẩn.

exports com.example.api;
Dòng này nói rằng: “Tôi, mô-đun, sẵn sàng chia sẻ mọi thứ nằm trong gói com.example.api”. Mọi thứ bên trong gói này và được đánh dấu public sẽ hiển thị với các mô-đun khác. Mọi thứ còn lại — chỉ dùng nội bộ.

requires java.sql;
Ở đây chúng ta khai báo rõ ràng với trình biên dịch: “Tôi cần mô-đun chuẩn java.sql để hoạt động.” Nếu không có điều này, trình biên dịch sẽ không cho phép dùng các lớp từ mô-đun đó.

Từ khóa bổ sung (để mở rộng hiểu biết)

  • opens <package>; — mở gói cho reflection (ví dụ, cho các thư viện tuần tự hóa như Jackson).
  • uses <service-interface>; — cho biết mô-đun sử dụng một service (interface).
  • provides <service-interface> with <implementation-class>; — cho biết mô-đun cung cấp một triển khai cho service.

Trong bài này, chúng ta sẽ tập trung vào exportsrequires — chúng cần thiết trong phần lớn các dự án học tập và dự án thực tế.

3. Ví dụ về module-info.java

Ví dụ 1. Mô-đun tối thiểu

module com.example.hello {
    exports com.example.hello.api;
}
  • Mô-đun này chỉ export gói com.example.hello.api.
  • Mọi thứ nằm, chẳng hạn, trong com.example.hello.internal sẽ bị ẩn khỏi các mô-đun khác, ngay cả khi ở đó có các lớp public.

Ví dụ 2. Mô-đun có phụ thuộc

module com.example.dbclient {
    exports com.example.db.api;
    requires java.sql;
}

Chúng ta có thể dùng JDBC, nhưng chỉ vì đã khai báo phụ thuộc vào java.sql.

Ví dụ 3. Nhiều gói được export

module com.example.library {
    exports com.example.library.api;
    exports com.example.library.utils;
}

Bạn có thể export bao nhiêu gói tùy ý (nhưng đừng export tràn lan — đó là ý nghĩa của mô-đun!).

4. Giới hạn và quy tắc

Tên mô-đun

  • Thường trùng với gói gốc (ví dụ, com.example.app).
  • Không được trùng với tên các mô-đun chuẩn (java.base, java.sql, v.v.).
  • Không được chứa khoảng trắng, ký tự đặc biệt, bắt đầu bằng chữ số, v.v.
  • Khuyến nghị: dùng tên miền đảo ngược của tổ chức hoặc dự án để tránh xung đột.

Một mô-đun — một module-info.java
Trong một mô-đun chỉ được phép có một tệp như vậy. Nếu có hai — trình biên dịch sẽ gây cho bạn “vụ bê bối mô-đun”.

Một gói chỉ được export từ một mô-đun
Cùng một gói không thể được export từ hai mô-đun khác nhau. Giống như có hai hộ chiếu cho một tên — nhà chức trách sẽ không chấp thuận.

Các gói bên trong mô-đun
Chỉ có thể export những gói thực sự tồn tại trong cấu trúc mã nguồn của mô-đun. Export một gói không tồn tại sẽ dẫn đến lỗi biên dịch.

5. Thực hành: tạo module-info.java trong dự án

Giả sử chúng ta có một dự án đơn giản với nội dung sau:

project-root/
└── src/
    └── com.example.greetings/
        ├── module-info.java
        └── com/
            └── example/
                └── greetings/
                    ├── api/
                    │   └── Greeter.java
                    └── internal/
                        └── SecretSauce.java

Bước 1. Tạo module-info.java

module com.example.greetings {
    exports com.example.greetings.api;
}

Bước 2. Thử dùng lớp từ gói internal trong mô-đun khác

Giả sử chúng ta có mô-đun thứ hai com.example.app, muốn truy cập SecretSauce:

module com.example.app {
    requires com.example.greetings;
}

import com.example.greetings.internal.SecretSauce; // LỖI!

Kết quả:
Trình biên dịch sẽ nói: “Gói com.example.greetings.internal không được mô-đun com.example.greetings export.” Dù lớp SecretSaucepublic, nó vẫn không khả dụng với các mô-đun khác.
Đây chính là tính đóng gói (encapsulation) ở cấp độ mô-đun!

Bước 3. Thử không khai báo requires

Nếu trong com.example.app chúng ta không viết requires com.example.greetings;, mà lại cố dùng lớp từ com.example.greetings.api, trình biên dịch sẽ báo lỗi:

package com.example.greetings.api is not visible

6. Các lỗi thường gặp khi làm việc với module-info.java

Lỗi 1: Tên mô-đun không khớp với cấu trúc dự án.
Nếu bạn đặt tên mô-đun là com.example.app, nhưng cấu trúc thư mục lại là src/main/java/app, trình biên dịch sẽ không hiểu bạn muốn gì. Tên mô-đun thường trùng với gói gốc và các thư mục phải phản ánh điều đó.

Lỗi 2: Export tất cả mọi thứ.
Chỉ export những gì thực sự cần hiển thị cho mô-đun khác. Đừng làm exports com.example; chỉ vì “cho nhanh”. Điều đó phá vỡ tính đóng gói.

Lỗi 3: Quên thêm requires.
Nếu bạn dùng các lớp từ mô-đun khác hoặc thư viện chuẩn (ví dụ, java.sql) nhưng quên khai báo phụ thuộc — sẽ có lỗi biên dịch.

Lỗi 4: Gói không tồn tại trong exports.
Nếu bạn viết exports com.example.foo; nhưng gói đó không tồn tại — trình biên dịch sẽ nói rằng bạn đang “export không khí”.

Lỗi 5: Lớp public nằm trong gói không được export.
Nếu lớp được khai báo public nhưng nằm trong gói không được export, lớp này chỉ hiển thị bên trong mô-đun. Đây không phải là lỗi, nhưng thường gây bất ngờ cho người mới.

Lỗi 6: Nhiều module-info.java trong một mô-đun.
Trong một mô-đun chỉ nên có một tệp module-info.java. Nếu có hai — trình biên dịch sẽ không thể build dự án.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION