Spring-Test: Explain Profile in Spring

Reprinted: http://www.jianshu.com/p/948c303b2253

foreword

When using Maven to package and deploy in a project, there are often too many configuration parameters (such as Nginx server information, ZooKeeper information, database connection, Redis server address, etc.), resulting in confusion between the actual network configuration parameters and the test server parameters. Once a parameter is forgotten to be modified during deployment, it must be repackaged and deployed, which is really a headache. So I thought of using the Profile in Spring to solve the problem described above, and record the way it is used here. If there is something wrong, please correct me! (Thanks) .
This article discusses Spring's Profile from the following three aspects:

  • What is Profile in Spring
  • Why use Profile
  • How to use Profile

1. What is Profile in Spring?

The Profile function in Spring actually came out as early as the version of Spring 3.1. It can be understood as the logical group name of the Bean we defined in the Spring container. Only when these Profiles are activated, will the corresponding profiles in the Profile be changed. The bean is registered with the Spring container. To give a more specific example, the Bean we defined before, when the Spring container starts, will load all the information to complete the creation of the bean; after using the Profile, it will update the definition of the bean. Fine-grained division, these defined beans are divided into several different groups. When the Spring container loads the configuration information, it first looks for the activated Profile, and then only loads the Bean information defined in the activated group. Bean definition information defined in an inactive Profile will not be loaded for bean creation.

2. Why use Profile

Since we are usually developing, we usually use a development database during development, a testing database during testing, and a database during actual deployment. The previous practice was to write this information in a configuration file. When I deployed the code to the test environment, I changed the configuration file to the test environment; when the test was completed, the project needed to be deployed to the live network, and the configuration information was required. It's really annoying to change it to the existing network. . . After using Profile, we can define three profiles, one for development, one for user testing, and one for user production, which correspond to three profiles respectively. When it is actually running, it only needs to give a parameter to activate the corresponding Profile, then the container will only load the activated profile, which can greatly save us the trouble of modifying the configuration information.

3.配置Spring profile

After introducing Profile and why to use it, let's take an example to demonstrate the use of Profile. Here we still use the traditional XML method to complete Bean assembly.

3.1 Maven dependencies required by the example

Since it is just a simple demonstration, there is no need to introduce the content of other modules of Spring, just the core 4 modules + test module.

     <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--指定Spring版本,该版本必须大于等于3.1-->
        <spring.version>4.2.4.RELEASE</spring.version>
        <!--指定JDK编译环境-->
        <java.version>1.7</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring.version}</version>
        </dependency>


        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

3.2 Example code

package com.panlingxiao.spring.profile.service;

/**
 * 定义接口,在实际中可能是一个数据源
 * 在开发的时候与实际部署的时候分别使用不同的实现
 */
public interface HelloService {

    public String sayHello();
}

Define the implementation class used by the production environment

package com.panlingxiao.spring.profile.service.produce;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.panlingxiao.spring.profile.service.HelloService;

/**
 * 模拟在生产环境下需要使用的类
 */
@Component
public class ProduceHelloService implements HelloService {

    //这个值读取生产环境下的配置注入
    @Value("#{config.name}")
    private String name;

    public String sayHello() {
        return String.format("hello,I'm %s,this is a produce environment!",
                name);
    }
}

Define the implementation class used under development

package com.panlingxiao.spring.profile.service.dev;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.panlingxiao.spring.profile.service.HelloService;

/**
 * 模拟在开发环境下使用类
 */
@Component
public class DevHelloService implements HelloService{

    //这个值是读取开发环境下的配置文件注入
    @Value("#{config.name}")
    private String name;

    public String sayHello() {
        return String.format("hello,I'm %s,this is a development environment!", name);
    }

}

Define the configuration Spring configuration file

<?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:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

    <!-- 定义开发的profile -->
    <beans profile="development">
        <!-- 只扫描开发环境下使用的类 -->
        <context:component-scan base-package="com.panlingxiao.spring.profile.service.dev" />
        <!-- 加载开发使用的配置文件 -->
        <util:properties id="config" location="classpath:dev/config.properties"/>
    </beans>

    <!-- 定义生产使用的profile -->
    <beans profile="produce">
        <!-- 只扫描生产环境下使用的类 -->
        <context:component-scan
            base-package="com.panlingxiao.spring.profile.service.produce" />
        <!-- 加载生产使用的配置文件 -->    
        <util:properties id="config" location="classpath:produce/config.properties"/>
    </beans>
</beans>

The configuration file used for development, dev/config.properties

    name=Tomcat

The configuration file used in production, produce/config.properties

name=Jetty

write test class

package com.panlingxiao.spring.profile.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.panlingxiao.spring.profile.service.HelloService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring-profile.xml")
/*
 * 使用注册来完成对profile的激活,
 * 传入对应的profile名字即可,可以传入produce或者dev
 */
@ActiveProfiles("produce")
public class TestActiveProfile {

    @Autowired
    private HelloService hs;

    @Test
    public void testProfile() throws Exception {
        String value = hs.sayHello();
        System.out.println(value);
    }
}

Activate dev running result.png

Activate produce run result.jpg

4. Several other ways to activate the Profile

The above describes how to use Profile and activate the specified Profile in the unit test environment. In addition to using the @ActiveProfiles annotation to activate the profile, Spring also provides several other activation profiles, which are used in actual development. .
Spring determines which profiles can be activated through two different properties (note: multiple profiles can be activated at the same time), one property is spring.profiles.active and spring.profiles.default. These two constant values ​​are defined in Spring's AbstractEnvironment, see the AbstractEnvironment source code:

    /**
     * Name of property to set to specify active profiles: {@value}. Value may be comma
     * delimited.
     * <p>Note that certain shell environments such as Bash disallow the use of the period
     * character in variable names. Assuming that Spring's {@link SystemEnvironmentPropertySource}
     * is in use, this property may be specified as an environment variable as
     * {@code SPRING_PROFILES_ACTIVE}.
     * @see ConfigurableEnvironment#setActiveProfiles
     */
    public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";

    /**
     * Name of property to set to specify profiles active by default: {@value}. Value may
     * be comma delimited.
     * <p>Note that certain shell environments such as Bash disallow the use of the period
     * character in variable names. Assuming that Spring's {@link SystemEnvironmentPropertySource}
     * is in use, this property may be specified as an environment variable as
     * {@code SPRING_PROFILES_DEFAULT}.
     * @see ConfigurableEnvironment#setDefaultProfiles
     */
    public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";

If the spring.profiles.active property is set, then Spring will preferentially use the corresponding value of this property to activate the Profile. When spring.profiles.active is not set, then Spring will activate the Profile according to the corresponding value of the spring.profiles.default property. If neither of the above two properties are set, then no task Profile will be activated, and only beans defined outside the Profile will be created. We found that these two attribute values ​​are actually the attributes defined in the Spring container, and we rarely directly operate the Spring container itself in actual development, so if we want to set these two attributes, we actually need to define them in special locations. Let the Spring container automatically read these locations and set them automatically. These locations are mainly defined as follows:

  • As initialization parameter of DispatcherServlet in SpringMVC
  • as an initialization parameter in the web application context
  • as an entry to JNDI
  • as an environment variable
  • as a system parameter of the virtual machine
  • Activation using @AtivceProfile

In the actual use process, we can define the default profile as the development environment. When actually deploying, the main need to define spring.profiles.active in the environment variable in the actual deployment environment server to let Spring automatically read the current The configuration information in the environment, so that the trouble of frequently modifying the configuration file in different environments can be well avoided.

refer to:

  1. Spring Framework Reference Documentation 4.2.5.RELEASE-- 6.13. Environment abstraction
  2. Spring Framework Reference Documentation 3.2.3.RELEASE --3.2 Bean Definition Profiles
  3. <<Spring In Action Fourth Edition>>

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326212010&siteId=291194637