Spring Boot đọc cấu hình từ hơn mười nguồn. Bài này về thứ tự giữa chúng, in ra từ chính ứng dụng.
application.yml
app:
ten: "dịch vụ đơn hàng"
cong: 8080
han-cho: 30s
YAML hoặc .properties đều được. YAML gọn hơn cho cấu hình phân cấp; properties ít cạm bẫy cú pháp hơn.
Quy ước tên: gạch nối, viết thường (han-cho). Spring có cơ chế "relaxed binding" nên hanCho, han_cho, HAN_CHO đều ánh xạ về cùng một thuộc tính — điều này quan trọng vì biến môi trường không dùng được gạch nối.
Thứ tự ưu tiên, in ra thật
1. server.ports
2. configurationProperties
3. servletConfigInitParams
4. servletContextInitParams
5. systemProperties (-D...)
6. systemEnvironment (biến môi trường)
7. random
8. application.yml
Danh sách này in bằng ((ConfigurableEnvironment) env).getPropertySources() — nguồn đứng trước thắng.
Cộng thêm hai nguồn không hiện trong danh sách trên vì tôi không dùng: tham số dòng lệnh (--app.ten=x) đứng gần đầu, và application-{profile}.yml đứng ngay trên application.yml.
Kiểm bằng thực nghiệm:
APP_TEN="từ biến môi trường" java -jar app.jar --spring.profiles.active=prod
app.ten = từ biến môi trường <- biến môi trường thắng
app.cong = 9090 <- từ application-prod.yml
app.ten có ở cả ba nơi (application.yml, application-prod.yml, biến môi trường) và biến môi trường thắng. app.cong không có trong biến môi trường nên giá trị của profile thắng file gốc.
Chú ý cách viết biến môi trường: APP_TEN ánh xạ về app.ten — chữ hoa, gạch dưới thay dấu chấm.
Profile
java -jar app.jar --spring.profiles.active=prod
SPRING_PROFILES_ACTIVE=prod java -jar app.jar
=== PROFILE ĐANG BẬT ===
[prod]
Profile làm hai việc:
Nạp thêm application-{profile}.yml, đè lên application.yml. Đây là hợp nhất, không phải thay thế — thuộc tính không khai lại vẫn giữ giá trị gốc.
Bật tắt bean:
@Component
@Profile("prod")
class GuiMailThat implements GuiMail { }
@Component
@Profile("!prod")
class GuiMailGia implements GuiMail { }
Đây là cách gọn nhất để thay cài đặt theo môi trường, và nó ghép với @Primary cùng @Qualifier ở bài 4.
Bật nhiều profile cùng lúc được: --spring.profiles.active=prod,asia. Cái sau đè cái trước.
Từ Boot 2.4, có thể gộp profile vào một tệp bằng --- và spring.config.activate.on-profile — tiện cho dự án nhỏ, nhưng tệp riêng vẫn dễ đọc hơn khi cấu hình dài.
Chọn nguồn cho từng loại giá trị
Đây là phần thực dụng nhất:
Vào application.yml: giá trị mặc định hợp lý cho môi trường phát triển, thứ hiếm khi đổi, cấu trúc cấu hình.
Vào application-prod.yml: khác biệt theo môi trường mà không nhạy cảm — mức log, kích thước pool, tên dịch vụ.
Vào biến môi trường: mọi thứ khác nhau giữa các lần triển khai — địa chỉ CSDL, cờ tính năng.
Vào kho bí mật: mật khẩu, khoá API, chứng chỉ. Đừng để trong tệp cấu hình, kể cả tệp không commit.
Nguyên tắc từ Twelve-Factor App: cấu hình khác nhau giữa các môi trường thì nằm ngoài mã, còn cấu trúc thì nằm trong.
@Value và giá trị mặc định
@Value("${app.ten}") String ten;
@Value("${app.khong-co:giá trị mặc định}") String thieu;
ten=từ application.yml | thiếu -> giá trị mặc định
Dấu hai chấm khai mặc định. Không có mặc định mà thuộc tính thiếu thì ứng dụng không khởi động được — và đó là hành vi đúng, giống nguyên tắc "chết ngay nếu thiếu" ở bài 56 sê-ri Go.
Nhưng @Value có ba nhược điểm, và bài mai sẽ nói về cách thay thế tốt hơn.
Đừng đọc cấu hình rải rác
@Value("${app.han-cho}") String hanCho; // ở lớp A
@Value("${app.han-cho}") String hanCho2; // ở lớp B
Cùng một thuộc tính khai ở nhiều nơi là chỗ để lệch nhau. Gom vào một lớp cấu hình — bài mai.
Bí mật
Đừng commit bí mật, kể cả trong application-dev.yml. Thêm application-local.yml vào .gitignore cho giá trị cá nhân.
Đừng log toàn bộ cấu hình. Endpoint /actuator/env lộ mọi thuộc tính — bài 10 sẽ nói cách khoá nó lại.
Spring Boot có che một phần: thuộc tính có tên chứa password, secret, key, token sẽ hiện thành ****** trong actuator. Nhưng nó dựa vào tên, nên app.mat-khau không được che.
Thử ba mươi giây
var src = ((ConfigurableEnvironment) env).getPropertySources();
src.forEach(s -> System.out.println(s.getName()));
In ra thứ tự nguồn cấu hình thật của ứng dụng bạn. Lần tới khi một giá trị "không chịu đổi", danh sách này cho biết nguồn nào đang thắng — thay vì đoán.
Ngày mai: @ConfigurationProperties — gom cấu hình thành đối tượng có kiểu.