Home  >  Article  >  Java  >  Detailed explanation of Spring's Profile

Detailed explanation of Spring's Profile

高洛峰
高洛峰Original
2017-02-27 15:46:261474browse

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.activeIf 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&#39;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&#39;t a datasource configured in JNDI
 assertNull(dataSource);
 }
 }

}

runs the shouldBeEmbeddedDatasource method and the test passes

Detailed explanation of Springs Profile

For more detailed articles on Spring’s Profile, please pay attention to the PHP Chinese website!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn