Use the @Profile annotation to configure and switch the development, testing and production envir...
write in front
In the actual enterprise development environment, the environment is often divided into: development environment, test environment and production environment, and each environment is basically isolated from each other, that is to say, the development environment, test environment and production environment are mutually exclusive. incompatible. In the previous development process, if the developer completed the corresponding functional module and passed the unit test, he would modify the configuration of the project into the test environment by manually modifying the configuration file, and publish it to the test environment for testing. After passing the test, modify the configuration to the production environment and publish it to the production environment. This way of manually modifying the configuration, on the one hand, increases the workload of development and operation and maintenance, and always manually modifying the configuration files is prone to problems. So, is there any way to solve these problems? The answer is: yes! This can be done completely with the @Profile annotation.
Follow the Glacier Technology WeChat public account, and reply to the " Spring Annotation " keyword to get the source code.
If the article is helpful to you, you are welcome to leave a message, like, watch and forward. Your support is the driving force for my continuous creation!
@Profile annotation
If there are multiple components of the same type in the container, you can also use the @Profile annotation to identify which bean to get, which is especially useful in scenarios where different variables are used in different environments. For example, the development environment, test environment, and production environment use different data sources. Without changing the code, you can use this annotation to switch the database to be connected to.
Proceed as follows:
Add the @Profile annotation to the bean, and its value attribute is the environment identifier, which can be customized
Create a container using the no-argument constructor
Set the container environment, whose value is the environment ID set in step 1
Set the container's configuration class
refresh container
Note: Steps 2, 4, and 5 are actually steps of the construction method with parameters, which is equivalent to disassembling the construction method with parameters, inserting a statement in it to set the container environment, which we can see in the source code of Spring, such as the following code.
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this();
register(annotatedClasses);
refresh();
}
Next, let's look at the source code of the @Profile annotation, as shown below.
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Profiles;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
String[] value();
}
Note: @Profile can be marked not only on methods, but also on configuration classes. If it is marked on the configuration class, all configurations in the entire configuration class will take effect only in the specified environment. If a bean is not annotated with the @Profile annotation, the bean will be registered with the IOC container in any environment
Environment construction
Next, let's build an environment that uses the @Profile annotation to configure and switch the development, testing, and production environments. Here, we take different data sources as an example. First, we add the c3p0 and MySQL driver dependencies in the pom.xml file as shown below.
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version></dependency><dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version></dependency>
After adding project dependencies, we create a new ProfileConfig configuration class in the project, and simulate the data sources of development, testing, and production environments in the ProfileConfig configuration class, as shown below.
package io.mykit.spring.plugins.register.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* @author binghe
* @version 1.0.0
* @description test multiple data sources
*/
@Configuration
public class ProfileConfig {
@Bean("devDataSource")
public DataSource dataSourceDev() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_dev");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
@Bean("testDataSource")
public DataSource dataSourceTest() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_test");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
@Bean("prodDataDource")
public DataSource dataSourceProd() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_prod");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
}
This class is relatively simple. The data source used in the development environment is annotated with @Bean("devDataSource"); the data source used in the test environment is annotated with @Bean("testDataSource"); The ("prodDataDource") annotation annotates the data source used in the production environment.
Next, we create the ProfileTest class and create a new testProfile01() method in the ProfileTest class for testing, as shown below.
package io.mykit.spring.test;
import io.mykit.spring.plugins.register.config.ProfileConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
import java.util.stream.Stream;
/**
* @author binghe
* @version 1.0.0
* @description test class
*/
public class ProfileTest {
@Test
public void testProfile01(){
//Create an IOC container
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProfileConfig.class);
String[] names = context.getBeanNamesForType(DataSource.class);
Stream.of(names).forEach(System.out::println);
}
}
Run the testProfile01() method of the ProfileTest class, and the output result information is as follows.
devDataSource
testDataSource
prodDataDource
It can be seen that three different data sources have been successfully registered in the IOC container, indicating that our environment has been successfully built.
Register beans according to the environment
After we successfully set up the environment, the next step is to register the corresponding beans in the IOC container according to different environments. That is to say, we need to implement the data source used in the development environment to register the development environment; the test environment to register the data source used in the test environment; the production environment to register the data source used in the production environment. At this point, the @Profile annotation shows its powerful features.
We add the @Profile annotation to each data source in the ProfileConfig class, as shown below.
@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_dev");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_test");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
@Profile("prod")
@Bean("prodDataDource")
public DataSource dataSourceProd() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_prod");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
We use the @Profile("dev") annotation to identify the devDataSource registered in the development environment; use the @Profile("test") annotation to identify the testDataSource registered in the test environment; use the @Profile("prod") annotation to identify the production environment Register prodDataDource in the environment.
At this point, we run the testProfile01() method of the ProfileTest class and find that the command line does not output the result information . It means that after we add @Profile annotations for different data sources, the bean will not be registered in the IOC container by default. We need to register the corresponding bean in the IOC container according to the environment display designation.
In other words: the bean marked with the environment through the @Profile annotation, only when the environment is activated, the corresponding bean will be registered in the IOC container.
What if we need a default environment?
At this point, we can use the @Profile("default") annotation to identify a default environment. For example, we identify the devDataSource environment as the default environment, as shown below.
@Profile("default")@Bean("devDataSource")
public DataSource dataSourceDev() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_dev");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver"); return dataSource;
}
At this point, we run the testProfile01() method of the ProfileTest class, and the output result information is as follows.
devDataSource
As you can see, we use the @Profile("default") annotation on the devDataSource data source to set it as the default data source, and the command line will output devDataSource when running the test method.
Next, we restore the @Profile("default") annotation of the devDataSource data source to the @Profile("dev") annotation, identifying it as a data source registered in a development environment.
So, how do we register the corresponding beans according to different environments?
The first way is to determine the environment according to the command line parameters. We can add the corresponding command line parameters when running the program. For example, if our current environment is the test environment, we can add the following command line parameters when running the program.
-Dspring.profiles.active=test
The second way is through the no-argument constructor of the AnnotationConfigApplicationContext class. We call the no-parameter constructor of AnnotationConfigApplicationContext in the program to generate the IOC container. Before the container is initialized, we set the corresponding environment for the IOC container, and then set the main configuration class for the IOC container. For example, we set up the IOC container as a production environment as shown below.
@Test
public void testProfile02(){
//Create an IOC container
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("prod");
context.register(ProfileConfig.class);
context.refresh();
String[] names = context.getBeanNamesForType(DataSource.class);
Stream.of(names).forEach(System.out::println);
}
At this point, we run the testProfile02() method, and the output result information is as follows.
prodDataDource
As you can see, the command line outputs prodDataDource, indicating that we have successfully set the IOC environment to the production environment.
@Profile can be marked not only on methods, but also on configuration classes. If it is marked on the configuration class, all configurations in the entire configuration class will take effect only in the specified environment. For example, we annotate the ProfileConfig class with the @Profile("dev") annotation as shown below.
@Profile("dev")
@Configuration
public class ProfileConfig {
/*********Code omitted *********/
}
Next, we run the testProfile02() method and find that no information is output on the command line.
This is because we specified the current environment as the production environment in the testProfile02() method, and the annotation on the ProfileConfig class is @Profile("dev"), indicating that all configurations in the ProfileConfig class will only take effect in the development environment. . So, no data source is registered to the IOC container at this time, and the command line will not print any information.
0 Comments