1. Application scenarios

In daily development work, we often need to deploy programs to different environments, such as Dev development environment, QA testing environment, Prod production environment, some of the configurations in these environments will be different, such as database configuration, Redis configuration, RabbitMQ configuration.

It would bea nightmare for programmers to have to change the configuration and rebuild it every time they switch publishing environments. For this scenario, Spring provides the @profile annotation to assemble different beans for different environments, so that implementations are built once but can be deployed to multiple environments.

2. Configure the profile bean

To better understand, let’s look at some code examples to understand how to use the Spring Profile, using database configuration as an example.

Note: The focus of this blog is to explain the use of @profile annotation, database operation is only to help understand @profile, so I won’t explain too much detail, but I will write a separate blog to explain

Suppose we have three environments (Dev, QA, and Prod) that use mysql as their database, but have different addresses, usernames, and passwords. How do we declare these beans in our Java configuration?

Configure profile beans in 2.1 Java Configuration

The first thing to understand is that the @Profile annotation was introduced in Spring 3.1, and in this release, the @Profile annotation can only be used at the class level.

So we can create a database configuration for each environment as follows:

Database configuration in Dev:

package chapter03.profile;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import javax.sql.DataSource;

@Configuration
@Profile("dev")
public class DevDataSourceConfig {
    @Bean
    public DataSource devDataSource(a) {
        System.out.println("This is dev DataSource");

        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        basicDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_action_db");
        basicDataSource.setUsername("dev");
        basicDataSource.setPassword("dev");

        returnbasicDataSource; }}Copy the code

To use the above code, add the following dependencies to pom.xml:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.7.0</version>
</dependency>
Copy the code

Note: If @profile (“dev”) is used at the class level, all beans in the class will be created when the Profile is dev.

Database configuration in QA:

package chapter03.profile;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import javax.sql.DataSource;

@Configuration
@Profile("qa")
public class QADataSourceConfig {
    @Bean
    public DataSource qaDataSource(a) {
        System.out.println("This is qa DataSource");

        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        basicDataSource.setUrl("jdbc:mysql://localhost:3307/mybatis_action_db");
        basicDataSource.setUsername("qa");
        basicDataSource.setPassword("qa");

        returnbasicDataSource; }}Copy the code

Database configuration in Prod:

package chapter03.profile;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import javax.sql.DataSource;

@Configuration
@Profile("prod")
public class ProdDataSourceConfig {
    @Bean
    public DataSource prodDataSource(a) {
        System.out.println("This is prod DataSource");

        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        basicDataSource.setUrl("jdbc:mysql://localhost:3308/mybatis_action_db");
        basicDataSource.setUsername("prod");
        basicDataSource.setPassword("prod");

        returnbasicDataSource; }}Copy the code

However, as of Spring 3.2, the @profile annotation can be used at the method level along with the @Bean annotation.

This allows us to combine the three configuration classes we just described into one (recommended), as follows:

package chapter03.profile;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {
    @Bean
    @Profile("dev")
    public DataSource devDataSource(a) {
        System.out.println("This is dev DataSource");

        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        basicDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_action_db");
        basicDataSource.setUsername("dev");
        basicDataSource.setPassword("dev");

        return basicDataSource;
    }

    @Bean
    @Profile("qa")
    public DataSource qaDataSource(a) {
        System.out.println("This is qa DataSource");

        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        basicDataSource.setUrl("jdbc:mysql://localhost:3307/mybatis_action_db");
        basicDataSource.setUsername("qa");
        basicDataSource.setPassword("qa");

        return basicDataSource;
    }

    @Bean
    @Profile("prod")
    public DataSource prodDataSource(a) {
        System.out.println("This is prod DataSource");

        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        basicDataSource.setUrl("jdbc:mysql://localhost:3308/mybatis_action_db");
        basicDataSource.setUsername("prod");
        basicDataSource.setPassword("prod");

        returnbasicDataSource; }}Copy the code

Note: Beans without a profile specified are always created, regardless of which profile is activated.

2.2 Configuring profile Beans in XML

We can also configure profile beans in XML through the profile attribute of the

element, as follows:

<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
       profile="dev">
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
          p:driverClassName="com.mysql.jdbc.Driver"
          p:url="jdbc:mysql://localhost:3306/mybatis_action_db"
          p:username="dev"
          p:password="dev"/>
</beans>
Copy the code

You can refer to this configuration to create profile XML files for qa and PROD environments respectively.

However, it is recommended to use nested

elements to configure the data sources for three environments in one XML file, as shown below:

<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <beans profile="dev">
        <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
              p:driverClassName="com.mysql.jdbc.Driver"
              p:url="jdbc:mysql://localhost:3306/mybatis_action_db"
              p:username="dev"
              p:password="dev"/>
    </beans>
    <beans profile="qa">
        <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
              p:driverClassName="com.mysql.jdbc.Driver"
              p:url="jdbc:mysql://localhost:3307/mybatis_action_db"
              p:username="qa"
              p:password="qa"/>
    </beans>
    <beans profile="prod">
        <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
              p:driverClassName="com.mysql.jdbc.Driver"
              p:url="jdbc:mysql://localhost:3308/mybatis_action_db"
              p:username="prod"
              p:password="prod"/>
    </beans>
</beans>
Copy the code

3. Activate the profile

So far, we have created three beans according to the dimension of the environment, but at runtime, only one bean will be created, depending on which profile is active.

So, how do we activate a profile?

Spring relies on two attributes when deciding which profile to activate:

  1. spring.profiles.active
  2. spring.profiles.default

Active has a higher priority than spring.profiles.default, that is, if spring.profiles.active is not configured, the value configured with spring.profiles.default is used. If spring.profiles.active is configured, the value of the spring.profiles.default configuration is no longer used.

If neither is configured, only beans that do not have a profile defined are created.

In a Web application, the code to set spring.profiles. Active in web.xml looks like this:

<context-param>
	<param-name>spring.profiles.active</param-name>
    <param-value>dev</param-value>
</context-param>
Copy the code

It can also be activated using code:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

context.getEnvironment().setActiveProfiles("dev");
Copy the code

Unit testing

Create a new Main class and add the following test code to its Main () method:

package chapter03.profile;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        context.getEnvironment().setActiveProfiles("dev"); context.register(DataSourceConfig.class); context.refresh(); context.close(); }}Copy the code

The following output is displayed:

This is dev DataSource

If you change the code to context.getenVironment ().setActiveProfiles(” QA “); , the output result is:

This is qa DataSource

If you change the code to context.getenVironment ().setActiveProfiles(“prod”); , the output result is:

This is prod DataSource

5. Source code and reference

Source code address: github.com/zwwhnly/spr… Welcome to download.

Java EE Development Disruptor: Spring Boot Combat by Wang Yunfei

Craig Walls: Spring In Action (4th Edition)