Home >Java >javaTutorial >Detailed explanation of Spring's Profile
Preface
The Profile function in Spring has actually been out as early as Spring 3.1. It can be understood as the logic of the beans we define in the Spring container. Group name. Only when these Profiles are activated, the beans corresponding to the Profile will be registered in the Spring container.
When you see the keyword Profile, maybe you have never looked at it directly, or you may have some vague impressions in your mind. For example, in addition to the Profile in Springmvc here, there is also the Profile tag in maven.
From a literal meaning, Profile means profile, so under what circumstances will the profile function be used, and what is the specific meaning of profile?
For example, for the database For configuration issues, from the perspective of development, you can use the embedded database and load test data (code examples will be given later). But in the eyes of testing, a database connection pool may be equipped like this
@Bean(destroyMethod="close") public DataSource dataSource () { BasicDataSource dataSource = new BasicDataSource(); dataSource.setUrl("jdbc:h2:tcp://dbserver/~/test"); dataSource.setDriverClassName("org.h2.Driver"); dataSource.setUsername("sa"); dataSource.setPassword("password"); dataSource.setInitialSize(20); dataSource.setMaxActive(30); return dataSource; }
Of course, there are also configurations in the production environment, etc. What else can you say about this kind of configuration method that lets a hundred flowers bloom? It silently deploys corresponding configuration files for this set of environments. We have always done this without profiles.
But now with Profile, we have one more choice, a more intelligent and worry-free configuration method. Through Profile configuration, Spring can decide whether to create a bean during the running phase according to the environment. The following is an example, mainly starting from the configuration and activation of Profile beans.
Profile bean configuration
Configuration through annotation @Profile
For the above example In the first case, in the development environment we configure a data source like this
@Bean(destroyMethod = "shutdown") public DataSource embeddedDataSource() { return new EmbeddedDatabaseBuilder() .addScript("classpath:schema.sql") .addScript("classpath:test-data.sql") .build(); }
Here EmbeddedDatabaseBuilder will be used to create an embedded database, and the schema is defined in In the schema.sql file under the class file
schema.sql
create table Things ( id identity, name varchar(100) );
a Things table is defined here Contains two fields
In addition to the schema file, you also need to load test data through test-data.sql
test-data.sql
insert into Things (name) values ('A')
I don’t know whether this @Bean is created in a development environment or a product environment. So we can use the annotation @Profile here to help us label this bean.
The bean profile function was introduced in Spring version 3.1, which allows you to define different beans into one or more profiles, and then when deploying the application, tell which profile to activate, the corresponding bean will be created.
For example here
@Configuration @Profile("dev") public class DevelopmentProfileConfig { @Bean(destroyMethod = "shutdown") public DataSource embeddedDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath:schema.sql") .addScript("classpath:test-data.sql") .build(); } }
pass @Profile("dev")
for the EmbedderDataSource bean Mark the bean to be created in the dev environment.
Note: 1. @Profile is loaded at the class level. If the dev profile is not activated, then all the corresponding beans in the class will not be created.
2. If the current dev environment is is activated, then beans that do not use @Profile will be created. If they are marked as other profiles such as prod, the corresponding beans will not be created
3. Starting from 3.2, @Profile can not only load classes At the level, you can also load the method. The specific code is as follows
package com.myapp; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.jndi.JndiObjectFactoryBean; @Configuration public class DataSourceConfig { @Bean(destroyMethod = "shutdown") @Profile("dev") public DataSource embeddedDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath:schema.sql") .addScript("classpath:test-data.sql") .build(); } @Bean @Profile("prod") public DataSource jndiDataSource() { JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("jdbc/myDS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean.getObject(); } }
Configuration through xml configuration file
In addition to simplicity The annotation method can be declared in the xml configuration file. The specific configuration is as follows
datasource-config.xml
<?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:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.php.cn/ http://www.php.cn/ http://www.php.cn/ http://www.php.cn/ http://www.php.cn/ http://www.php.cn/"> <beans profile="dev"> <jdbc:embedded-database type="H2"> <jdbc:script location="classpath:schema.sql" /> <jdbc:script location="classpath:test-data.sql" /> </jdbc:embedded-database> </beans> <beans profile="prod"> <jee:jndi-lookup lazy-init="true" jndi-name="jdbc/myDatabase" resource-ref="true" proxy-interface="javax.sql.DataSource" /> </beans> </beans>
Two environments and corresponding profiles are declared here.
profile activation
Although we have configured the profile, how to activate the corresponding environment. Here we need two properties spring.profile.active
and spring.profile.default
.
If spring.profile.active
is assigned, spring.profile.default
will not take effect. If spring.profie.active
If no value is assigned, the default value set by spring.profile.default
will be used. Of course, if neither is set, only those beans defined in the corresponding profile will be created.
There are many ways to set these two properties:
As the initialization parameter of DispactcherServlet
As the Web application context parameter
As a JNDI entry
As an environment variable
As a JVM system property
On the integration test class, use the @ActiveProfiles annotation to set it
For example, we are on the web The code in .xml can be declared as follows
<?xml version="1.0" encoding="UTF-8"?> <web -app version="2.5" ...> //为上下文设置默认的profile <context-param> <param-name>spring.profile.default</param-name> <param-value>dev</param-value> </context-param> ... <servlet> ... //为Serlvet设置默认的profile <init-param> <param-name>spring-profiles.default</param-name> <param-value>dev</param-value> </init-prama> ... <web-app>
This way you can specify which environment needs to be started and prepare the corresponding beans.
In addition, for testing, why does spring provide a simple annotation using @ActiveProfiles, which can specify which profile should be activated when running the test. For example, the test class here DevDataSourceTest
package profiles; import static org.junit.Assert.*; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import javax.sql.DataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.myapp.DataSourceConfig; public class DataSourceConfigTest { @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=DataSourceConfig.class) @ActiveProfiles("dev") public static class DevDataSourceTest { @Autowired private DataSource dataSource; @Test public void shouldBeEmbeddedDatasource() { assertNotNull(dataSource); JdbcTemplate jdbc = new JdbcTemplate(dataSource); List<String> results = jdbc.query("select id, name from Things", new RowMapper<String>() { @Override public String mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getLong("id") + ":" + rs.getString("name"); } }); assertEquals(1, results.size()); assertEquals("1:A", results.get(0)); } } @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=DataSourceConfig.class) @ActiveProfiles("prod") public static class ProductionDataSourceTest { @Autowired private DataSource dataSource; @Test public void shouldBeEmbeddedDatasource() { // should be null, because there isn't a datasource configured in JNDI assertNull(dataSource); } } @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:datasource-config.xml") @ActiveProfiles("dev") public static class DevDataSourceTest_XMLConfig { @Autowired private DataSource dataSource; @Test public void shouldBeEmbeddedDatasource() { assertNotNull(dataSource); JdbcTemplate jdbc = new JdbcTemplate(dataSource); List<String> results = jdbc.query("select id, name from Things", new RowMapper<String>() { @Override public String mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getLong("id") + ":" + rs.getString("name"); } }); assertEquals(1, results.size()); assertEquals("1:A", results.get(0)); } } @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:datasource-config.xml") @ActiveProfiles("prod") public static class ProductionDataSourceTest_XMLConfig { @Autowired(required=false) private DataSource dataSource; @Test public void shouldBeEmbeddedDatasource() { // should be null, because there isn't a datasource configured in JNDI assertNull(dataSource); } } }
runs the shouldBeEmbeddedDatasource method and the test passes
For more detailed articles on Spring’s Profile, please pay attention to the PHP Chinese website!