Spring Profile là gì? bạn có thật sự hiểu rõ?

Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu về Spring Profile là gì, và áp dụng nó ra làm sao trong Spring Framework.

Profile là một trong những khái niệm cốt lỗi của Spring cho phép chúng ta ánh xạ các bean đến các môi trường khác nhau. Hiểu đơn giản là một bean chỉ được khởi tạo và sử dụng trong một môi trường nhất định ví dụ như dev, test, production.

Sử dụng @Profile trên Bean

@Profile annotation được sử dụng để chỉ định profile cho một bean cụ thể. Annotation này nhận vào tham số là tên của một hoặc nhiều profile được áp dụng cho bean. 

Giả sử chúng ta có một bean chỉ nên hoạt động trong môi trường development nhưng không hoạt động trên môi trường production. Chúng ta sẽ chú thích bean này với dev profile, và sau đó bean này chỉ xuất hiện trong Spring IoC container khi ứng dụng chạy với development profile là một trong những profile được active.

@Component
@Profile("dev")
public class DevDatasourceConfig {
    @Bean
    public DataConfig dataConfig() {
         return new DataConfig();
    }
}

Khi @Profile được sử dụng trên class-level thì tất cả các bean được định nghĩa bên trong nó sẽ thuộc về profile được chỉ định trong @Profile. Trong trường hợp này dataConfig bean sẽ thuộc về profile dev.

Chúng ta có thể chỉ sử dụng @Profile ở các method khởi tạo bean, lúc này chỉ những bean được tạo bởi method này mới thuộc về profile được chỉ định.

Chúng ta cũng có thể sử dụng toán tử NOT trong @Profile như sau

@Component
@Profile("!prod")
public class DatasourceConfig

Với đoạn code trên thì các DatasourceConfig bean chỉ hoạt động khi profile không phải là prod.

Tạo Bean thuộc về nhiều Profile

Đôi lúc, có một số bean có thể được dùng chung ở nhiều profile khác nhau, chúng ta có thể làm điều này thông qua @Profile với giá trị là một danh sách các profile mà bean này thuộc về. 

@Component
@Profile({"dev", "testdb"})
public class DatasourceConfig {
    @Bean
    public DataConfig dataConfig() {
         return new DataConfig();
    }
}

Trong đoạn code trên thì tất cả các bean được ra từ DatasourceConfig class đều sẽ được khởi tạo và đăng ký với Spring IoC container khi có ít nhất một trong 2 profile dev hoặc testdb được active.

Bạn hoàn toàn có thể sử dụng toán tử NOT như sau:

@Component
@Profile({"dev", "!testdb"})
public class DatasourceConfig {
    @Bean
    public DataConfig dataConfig() {
         return new DataConfig();
    }
}

Như vậy thì các DatasourceConfig bean chỉ được khởi tạo khi dev được active hoặc testdb không được active.

Khai báo profile trong XML

Tương tự như trên, nhưng nếu bạn dùng XML để cấu hình thì có thể định nghĩa profile trong thẻ <beans> với thuộc tính profile

<beans profile="dev">
    <bean id="devDatasourceConfig" 
      class="org.baeldung.profiles.DevDatasourceConfig" />
</beans>

Tương tự chúng ta có thể khai báo một bean thuộc về nhiều profile khác nhau thông qua thẻ <beans> với thuộc tính profile liệt kê một danh sách các profile cách nhau bởi dấu phẩy.

<beans profile="dev, testdb">
    <bean id="DatasourceConfig" 
      class="org.baeldung.profiles.DevDatasourceConfig" />
</beans>

Active Profile

Sau khi định nghĩa các bean với các profile tương ứng thì chúng ta tiến hành active một profile dựa theo môi trường hoặc hoàn cảnh cụ thể, các bean tương ứng với profile được active sẽ được khởi tạo và đăng ký với Spring IoC container.

Để active một profile chúng ta có nhiều cách mà chúng ta sẽ tìm hiểu ngay sau đây

Thông qua WebApplicationInitializer Interface

Trong các ứng dụng web, WebApplicationInitializer được sử dụng để cấu hình ServletContext, đây cũng là nơi tiện dụng để chúng ta có thể active một profile.

@Configuration
public class MyWebApplicationInitializer 
  implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
 
        servletContext.setInitParameter(
          "spring.profiles.active", "dev");
    }
}

Thông qua ConfigurableEnvironment

Chúng ta cũng có thể active profile trực tiếp thông qua ConfigurableEnvironment

@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("someProfile");

Context Parameter trong web.xml

Tương tự, chúng ta có thể active profile trong file web.xml của một ứng dụng web sử dụng context parameter:

<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>

Chúng ta có thể active nhiều profile với context parameter như sau:

<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, testdb</param-value>
</context-param>

 JVM System Parameter

Profile cũng có thể active bằng cách cài đặt tham số trên JVM system paramter lúc khởi chạy chương trình.

-Dspring.profiles.active=dev

Tương tự bạn cũng có thể active nhiều profile với JVM system

-Dspring.profiles.active=dev, testdb

Environment Variable

Nếu bạn đang sử dụng hệ điều hành Unix, profile có thể active thông qua biến môi trường như sau

export spring_profiles_active=dev, testdb

Maven Profile

Nếu dự án của bạn đang sử dụng Maven dể build và quản lý các dependency thì bạn có thể cấu hình trực tiếp trong file POM.XML thông qua thuộc tính spring.profiles.active.

<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ị này sẽ được sử dụng để thay thế spring.profiles.active@ trong tệp cấu hình application.properties hay application.yml tương ứng.

[email protected]@

Bây giờ chúng ta cần kích hoạt tính năng lọc tài nguyên trong pom.xml:

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
    ...
</build>

Cuối cùng thêm một -P parameter để chuyển đổi sang profile bạn mong muốn. Ví dụ 

mvn clean package -Pprod

Lệnh cmd trên sẽ đóng gói ứng dụng chi prod profile. Nó cũng áp dụng giá trị spring.profiles.active giá trị là prod khi ứng dụng chạy.

@ActiveProfile trong Unit Test

@ActiveProfiles annotation được sử dụng để chỉ định một profile cần thiết khi tải ApplicationContext cho các test class.

@ExtendWith(SpringExtension.class)
@ActiveProfiles("prod")
@ContextConfiguration
public class ActiveProfileTest {
------
} 

Profile mặc định trong Spring

Nếu một bean được định nghĩa mà không được chỉ định một profile cụ thể thì nó sẽ thuộc về default profile.

Spring cũng cho phép bạn cung cấp một profile mặc định khi không có profile nào được active thì nó sẽ được sử dụng thông qua thuộc tính spring.profiles.default trong file cấu hình application.properties hoặc application.yml.

spring.profiles.default=dev

Đoạn code trên sẽ giúp chúng ta định nghĩa một profile mặc định là dev, Khi không có profile nào được active thì dev sẽ được dùng trong ứng dụng.

Kiểm tra Active Profile

Để có thể biết những profile nào đang được active trong ứng dụng chúng ta có thể sử dụng Environment hoặc spring.active.profile.

public class ProfileManager {
    @Autowired
    private Environment environment;

    public void getActiveProfiles() {
        for (String profileName : environment.getActiveProfiles()) {
            System.out.println("Currently active profile - " + profileName);
        }  
    }
}

Hoặc 

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

Thuộc tính activeProfile sẽ chứa tên các profile đang active, nếu có nhiều hơn một profile được avtive thì chúng sẽ được ngăn cách nhau bởi dấu phẩy.

Tuy nhiên, trong trường hợp chúng ta không có một profile nào đang active với đoạn code trên của chúng ta sẽ gây ra lỗi IllegalArgumentException. Để tránh điều này chúng ta có thể định nghĩa một giá trị mặc định cho nó thông qua:

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

Cấu hình data source cho các môi trường khác nhau 

Giả sử chúng ta cần phải cấu hình data source cho cả 2 môi trường development và production. Trong trường hợp này chúng ta sẽ tạo 2 bean riêng biệt dùng để cấu hình cho mỗi môi trường. Và khi chạy ứng dụng chúng ta chỉ cần chỉ định rõ môi trường thì bean đó sẽ tự động được áp dụng để lấy các thông tin data source.

Đầu tiên chúng ta cần tạo một interface 

public interface DatasourceConfig {
    public void setup();
}

Tiếp đến là 2 class implment từ DatasourceConfig và cấu hình cho từng môi trường

@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 môi trường production

@Component
@Profile("production")
public class ProductionDatasourceConfig implements DatasourceConfig {
    @Override
    public void setup() {
       System.out.println("Setting up datasource for PRODUCTION environment. ");
    }
}

Giờ đây chúng ta chỉ cần sử dụng chúng thông qua DatasourceConfig và profile active.

public class SpringProfilesWithMavenPropertiesIntegrationTest {
    @Autowired
    DatasourceConfig datasourceConfig;

    public void setupDatasource() {
        datasourceConfig.setup();
    }
}

Khi bạn chạy ứng dụng với profile là dev và gọi đến hàm setupDatasource() thì kết quả sẽ như sau:

Setting up datasource for DEV environment.

Profile Trong Spring Boot

Spring Boot hỗ trợ tất cả các cách cấu hình từ nảy giờ chúng ta đã tìm hiểu, ngoài ra nó còn cung cấp thêm một số cách cấu hình khác. 

Sử dụng thuộc tính spring.profiles.active trong file application.yml hoặc application.properties sẽ khiến spring boot tự động quét và active với profile tương ứng được chỉ định.

spring.profiles.active=dev

Active nhiều Profile trong Spring Boot

Để active nhiều profile cùng lúc chúng ta cũng có thể chỉ định chúng trong spring.profiles.active ngăn cách nhau bởi dấu phẩy

spring.profiles.active=dev,hsqldb

Thêm active profile trong Spring

Thuộc tính spring.profiles.include có thể dùng để thêm các active profile ngoài các active profile được chỉ định trong spring.profiles.active.

---
my.property: fromyamlfile
---
spring.profiles: prod
spring.profiles.include: proddb,prodmq

Khi chúng ta khởi chạy ứng dụng với profile là prod, thì proddbprodmq cũng được active theo đó.

Ngoài ra chúng ta cũng có thể cấu hình thông qua đoạn mã code sau

SpringApplication.setAdditionalProfiles("dev");

Để cài đặt profile trong Spring Boot sử dụng Maven chúng ta có thể chỉ định tên của profile trong spring-boot-maven-plugin chứa trong file POM.XML

<plugins>
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <profiles>
                <profile>dev</profile>
            </profiles>
        </configuration>
    </plugin>
    ...
</plugins>

Nhưng một tính năng quan trọng hơn hết mà Spring Boot mang đến cho chúng ta là tạo ra các tệp cấu hình theo từng profile cụ thể, chúng ta cần tuân thủ theo định dạng: application-{profile}.properties.

Spring Boot sẽ tự động tải tất cả các thuộc tính trong application.properties cho tất cả các profile, và một trong những tệp cấu hình tương ứng với từng profile.

Ví dụ chúng ta có 2 file application-dev.properties application-production.properties ngoài application.properties.

Trong application-production.properties chứa các thông tin sau:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db
spring.datasource.username=root
spring.datasource.password=root

Trong application-dev.properties

spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa

Như vậy khi chúng ta chạy ứng dụng với profile dev hay production thì các thông tin tương ứng trong application-dev.properties application-production.properties sẽ được áp dụng.

Lưu ý, nếu cả trong application.properties và application-{profile}.properties đều chỉ định cùng một thuộc tính thì thì giá trị trên application-{profile}.properties sẽ được dùng.

Kết bài

Như vậy, chúng ta đã tìm hiểu xong khái niệm về profile, cách để tạo và active một profile trong Spring và Spring Boot Framework. Hy vọng kiến thức này có thể giúp bạn cấu hình môi trường một cách tiện lợi hơn trong thời gian tới làm việc với Spring Boot.

Nguồn tham khảo

https://www.concretepage.com/spring-5/activeprofiles-example-spring-test

https://stackoverflow.com/questions/37700352/setting-the-default-active-profile-in-spring-boot/37700521

https://docs.spring.io/spring-boot/docs/1.2.0.M1/reference/html/boot-features-profiles.html

https://reflectoring.io/spring-boot-profiles/

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html

https://www.baeldung.com/spring-profiles

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x