Post

Spring Profiles

1. Giới thiệu

Trong hướng dẫn này, chúng ta sẽ tập trung vào việc giới thiệu Profile trong Spring. Bạn có thể tìm hiểu thêm ở đâu đó trên internet hoặc ngay tại trang chủ chính thống của Spring.

Profile là một tính năng cốt lõi của Spring Framework - cho phép chúng ta ánh xạ các Bean của mình thành các Profile khác nhau - ví dụ: dev, test và prod.

Sau đó, chúng ta có thể kích hoạt các profile khác nhau đó trong các môi trường khác nhau để chỉ định việc khởi động các bean mà chúng ta cần.

2. Làm việc với Profile

Sử dụng @Profile trên Bean

Hãy bắt đầu đơn giản và xem xét cách chúng ta có thể làm cho một Bean thuộc một Profile cụ thể.

Chúng ta sử dụng chú thích @Profile - ánh xạ (mapping) Bean tới profile cụ thể đó; annotation chỉ lấy tên của một (hoặc nhiều) profile.

Hãy xem xét một kịch bản cơ bản: Chúng ta có một bean chỉ nên hoạt động trong quá trình phát triển nhưng không được triển khai trong sản xuất.

Chúng ta chú thích bean đó bằng dev Profile và nó sẽ chỉ hiện diện trong container trong quá trình phát triển. Trong sản xuất, dev đơn giản là sẽ không được hoạt động:

1
2
3
4
5
@Component
@Profile("dev")
public class DevDatasourceConfig{

}

Là một phụ chú nhanh, tên Profile cũng có thể được đặt trước bằng toán tử NOT, ví dụ: !Dev để loại trừ chúng khỏi Profile. Trong ví dụ, component chỉ được kích hoạt nếu dev Profile không hoạt động:

1
2
3
4
5
@Component
@Profile("!dev")
public class DevDatasourceConfig {

}

Khai báo Profile trong XML

Profile cũng có thể được cấu hình bằng XML. Thẻ có thuộc tính Profile nhận các giá trị được phân tách bằng dấu phẩy của các Profile hiện hành:

1
2
3
4
<beans profile="dev">
    <bean id="devDatasourceConfig" 
      class="com.thecorleone.profiles.DevDatasourceConfig" />
</beans>

Thiết lập Profile

Bước tiếp theo là kích hoạt và thiết lập các Profile để các bean tương ứng được đăng ký trong container. Điều này có thể được thực hiện theo nhiều cách khác nhau mà chúng ta sẽ khám phá trong các phần sau.

Kết hơp với WebApplicationInitializer Interface

Trong các ứng dụng web, WebApplicationInitializer có thể được sử dụng để cấu hình ServletContext. Đây cũng là một vị trí rất thuận tiện để đặt các active Profile của chúng ta:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class MyWebApplicationInitializer 
  implements WebApplicationInitializer {
 
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
 
        servletContext.setInitParameter(
          "spring.profiles.active", "dev");
    }
}

Kết hợp với ConfigurableEnvironment

Chúng ta cũng có thể đặt Profile trực tiếp trên môi trường:

1
2
3
4
@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("someProfile");

Context Parameter trong web.xml Tương tự, chúng ta có thể xác định active Profile trong file web.xml của ứng dụng web, sử dụng tham số ngữ cảnh (context parameter):

1
2
3
4
5
6
7
8
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/app-config.xml</param-value>
</context-param>
<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>dev</param-value>
</context-param>

Tham số hệ thống - JVM system parameter

Tên Profille cũng có thể được chuyển vào thông qua tham số hệ thống JVM. Các Profile này sẽ được kích hoạt trong quá trình khởi động ứng dụng:

1
-Dspring.profiles.active=dev

Biến môi trường - Environment Variable

Trong môi trường Unix, các cấu hình cũng có thể được kích hoạt thông qua biến môi trường:

1
export spring_profiles_active=dev

Maven Profile

Spring Profile cũng có thể được kích hoạt thông qua Maven Profile bằng cách chỉ định thuộc tính cấu hình spring.profiles.active. Trong mọi Maven Profile, chúng ta có thể đặt thuộc tính spring.profiles.active:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<profiles>
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <spring.profiles.active>dev</spring.profiles.active>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <spring.profiles.active>prod</spring.profiles.active>
        </properties>
    </profile>
</profiles>

Giá trị của nó sẽ được sử dụng để thay thế trình giữ chỗ @spring.profiles.active@ trong application.properties:

1
spring.profiles.active=@spring.profiles.active@

Bây giờ chúng ta cần bật tính năng filter tài nguyên trong pom.xml:

1
2
3
4
5
6
7
8
9
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
    ...
</build>

và thêm thông số -P để chuyển đổi cấu hình Maven sẽ được áp dụng:

1
mvn clean package -Pprod

Lệnh này sẽ đóng gói ứng dụng cho profile sản phẩm. Nó cũng áp dụng giá trị spring.profiles.active cho ứng dụng này khi nó đang chạy.

@ActiveProfile trong kiểm thử

Các kiểm thử giúp bạn dễ dàng chỉ định Profile nào đang hoạt động bằng cách sử dụng chú thích @ActiveProfile để kích hoạt Profile cụ thể:

1
@ActiveProfiles("dev")

Cho đến đây, chúng ta đã xem xét nhiều cách kích hoạt Profile. Bây giờ chúng ta hãy xem cái nào có mức độ ưu tiên hơn cái kia và điều gì sẽ xảy ra nếu chúng ta sử dụng nhiều hơn một cái, từ mức độ ưu tiên cao nhất đến thấp nhất:

  1. Context parameter trong web.xml
  2. WebApplicationInitializer
  3. JVM System parameter
  4. Environment variable
  5. Maven profile

3. Truy cập Active Profile

Các active Profile của Spring thúc đẩy hành vi của chú thích @Profile để bật/tắt bean. Tuy nhiên, chúng ta cũng có thể truy cập danh sách các active Profile theo chương trình.

Chúng ta có hai cách để làm điều đó, sử dụng Environment hoặc spring.active.profile.

Sử dụng Environment

Chúng ta có thể truy cập các active profile từ đối tượng Enviroment bằng cách:

1
2
3
4
5
6
7
8
9
10
public class ProfileManager {
    @Autowired
    private Environment environment;
 
    public void getActiveProfiles() {
        for (String profileName : environment.getActiveProfiles()) {
            System.out.println("Currently active profile - " + profileName);
        }  
    }
}

Sử dụng spring.active.profile

Ngoài ra, chúng ta có thể truy cập các Profile bằng cách chèn thuộc tính spring.profiles.active:

1
2
@Value("${spring.profiles.active}")
private String activeProfile;

Ở đây, biến ActiveProfile của chúng ta sẽ chứa tên của Profile hiện đang hoạt động và nếu có nhiều Profile, nó sẽ chứa tên của chúng được phân tách bằng dấu phẩy.

Tuy nhiên, chúng ta nên xem xét điều gì sẽ xảy ra nếu không có active Profile nào cả. Với code của chúng ta ở trên, việc không có active Profile sẽ ngăn không cho tạo ngữ cảnh ứng dụng. Điều này sẽ dẫn đến IllegalArgumentException do trình giữ chỗ bị thiếu để đưa vào biến.

Để tránh điều đó xảy ra, chúng ta có thể xác định một giá trị mặc định:

1
2
@Value("${spring.profiles.active:}")
private String activeProfile;

Và nếu không có Profile nào đang hoạt động, thì ActiveProfile của chúng ta sẽ chỉ chứa một chuỗi trống. Nếu chúng ta muốn truy cập danh sách Profile giống như trong ví dụ trước, chúng ta có thể thực hiện bằng cách tách biến ActiveProfile:

1
2
3
4
5
6
7
8
9
10
public class ProfileManager {
    @Value("${spring.profiles.active:}")
    private String activeProfiles;
 
    public String getActiveProfiles() {
        for (String profileName : activeProfiles.split(",")) {
            System.out.println("Currently active profile - " + profileName);
        }
    }
}

Một ví dụ demo thực tế - Sử dụng Profile để cấu hình nguồn dữ liệu

Bây giờ, những điều cơ bản đã không còn nữa, chúng ta hãy xem một ví dụ thực tế.

Hãy xem xét một tình huống mà chúng ta phải duy trì cấu hình nguồn dữ liệu cho cả dev và production environment. Tạo một interface DatasourceConfig cần được triển khai bởi cả hai quá trình triển khai nguồn dữ liệu:

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface DatasourceConfig {
    public void setup();
}

Sau đây  cấu hình cho dev environment:
@Component
@Profile("dev")
public class DevDatasourceConfig implements DatasourceConfig {
    @Override
    public void setup() {
        System.out.println("Setting up datasource for DEV environment. ");
    }
}

Và cấu hình cho production environment:

1
2
3
4
5
6
7
8
@Component
@Profile("production")
public class ProductionDatasourceConfig implements DatasourceConfig {
    @Override
    public void setup() {
       System.out.println("Setting up datasource for PRODUCTION environment. ");
    }
}

Bây giờ, hãy tạo một test và inject vào Interface DatasourceConfig của chúng ta - tùy thuộc vào active Profile, Spring sẽ inject DevDatasourceConfig hoặc ProductionDatasourceConfig:

1
2
3
4
5
6
7
8
public class SpringProfilesWithMavenPropertiesIntegrationTest {
    @Autowired
    DatasourceConfig datasourceConfig;
 
    public void setupDatasource() {
        datasourceConfig.setup();
    }
}

Khi dev Profile đang hoạt động, Spring sẽ inject đối tượng DevDatasourceConfig và khi gọi phương thức setup(), ta sẽ được kết quả:

1
Setting up datasourc for DEV environment.

Trong bài viết này, chúng ta đã thảo luận về cách xác định Profile trên bean và cách kích hoạt Profile phù hợp trong ứng dụng. Cuối cùng, chúng ta đã xác thực sự hiểu biết của mình về các Profile bằng một ví dụ đơn giản nhưng thực tế.

Với các chi tiết được tổng hợp thông qua bài viết này, chắc chắn bạn đã tích luỹ được riêng cho mình một vốn kiến thức nhất định. Hãy tiếp tục tự nghiên cứu và phát triển thêm nhé.

Bài viết mang tính chất “ghi chú, lưu trữ, chia sẻ và phi lợi nhuận”.
Nếu bạn thấy hữu ích, đừng quên chia sẻ với bạn bè và đồng nghiệp của mình nhé!

Happy coding! 😎 👍🏻 🚀 🔥

Đọc thêm:

This post is licensed under CC BY 4.0 by the author.