前言
Spring中的Profile功能其實早在Spring 3.1的版本就已經出來,它可以理解為我們在Spring容器中所定義的Bean的邏輯組名稱,只有當這些Profile被激活的時候,才會將Profile中所對應的Bean注冊到Spring容器中。
看到Profile這個關鍵字,或許你從來沒有正眼瞧過他,又或者腦海中有些模糊的印象,比如除了這里Springmvc中的Profile,maven中也有Profile的標簽。
從字面意思來看,Profile表示側面,那什么情況下才會用到側面這個功能呢,而側面具體又有什么含義呢
打一個比方,對于數據庫的配置問題,在開發的眼中可以使用嵌入的數據庫,并且加載測試數據(后面會給出代碼示例)。但是在測試的眼中,可能會配一個數據庫連接池類似這樣
1
2
3
4
5
6
7
8
9
10
11
|
@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; } |
當然還有產品環境下的配置等等。對于這種百花齊放的配置方式你還能說什么,默默的為這一套套的環境都部署相應的配置文件啊,沒有profile這套我們一直都是這么做。
但是現在有了Profile,我們就多了一種選擇,一種更加智能省心的配置方式。通過Profile配置,Spring可以在根據環境在運行階段來決定bean的創建與否,先舉例如下,主要從Profile bean的配置和激活來展開。
Profile bean的配置
通過注解@Profile配置
對于上面比方中的第一種情況,在開發環境中我們配置一個數據源可能是這樣的
1
2
3
4
5
6
7
|
@Bean (destroyMethod = "shutdown" ) public DataSource embeddedDataSource() { return new EmbeddedDatabaseBuilder() .addScript( "classpath:schema.sql" ) .addScript( "classpath:test-data.sql" ) .build(); } |
這里會使用EmbeddedDatabaseBuilder創建一個嵌入式數據庫,模式定義在類文件下的schema.sql文件中
schema.sql
1
2
3
4
|
create table Things ( id identity, name varchar (100) ); |
這里定義了一張Things表包含了兩個字段
除了模式文件,還需要通過test-data.sql加載測試數據
test-data.sql
1
|
insert into Things ( name ) values ( 'A' ) |
對于這個@Bean完全不知道是放在開發的環境下創建還是產品的環境下。所以我們這里可以使用注解@Profile幫助我們為這個bean打上標識。
從Spring 3.1版本中就引入了bean profile的功能,可以讓你將不同的bean定義到一個或者多個profile里,然后在部署應用時告知要激活那個profile,則相應的bean就會被創建。
比如這里
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@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(); } } |
通過@Profile("dev")
為EmbedderDataSource bean標記為dev環境下要創建的bean。
注意:1. @Profile被加載類級別上,如果dev profile沒有被激活,那么類中對應的所有bean就不會被創建
2. 如果當前是dev環境被激活了,那么對于沒有使用@Profile的bean都會被創建,被標記為其他的profile如prod,則不會創建相應的bean
3. 從3.2開始@Profile不僅僅可以加載類級別上,還可以加載方法上,具體代碼如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
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(); } } |
通過xml配置文件配置
除了簡單的注解方式,我們哈可以通過在xml配置文件中聲明的方式,具體配置如下
datasource-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<? 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.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> < 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 > |
這里分別聲明了兩種環境以及對應的profile。
profile激活
雖然我們已經配置好了profile,但是如何激活相應的環境呢。這里我們需要兩個屬性spring.profile.active
以及spring.profile.default
。
如果spring.profile.active
被賦值了,則spring.profile.default
就不會起作用,如果spring.profie.active
沒有賦值,則使用默認的spring.profile.default
設置的值。當然,如果兩者都沒有設置的話,則只會創建那些定義在相應的profile中的bean。
設置這兩個屬性的方式有很多:
作為DispactcherServlet的初始化參數
作為Web應用上下文參數
作為JNDI條目
作為環境變量
作為JVM的系統屬性
在集成測試類上,使用@ActiveProfiles注解設置
比如我們在web.xml中可以聲明代碼如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?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> |
這樣就可以指定需要啟動那種環境,并準備相應的bean。
另外對于測試,spring為什么提供了一個簡單的注解可以使用@ActiveProfiles,它可以指定運行測試的時候應該要激活那個profile。比如這里的測試類DevDataSourceTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
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); } } } |
運行shouldBeEmbeddedDatasource方法,測試通過
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。