1. 使用場景
在日常的開發工作中,我們經常需要將程序部署到不同的環境,比如Dev開發環境,QA測試環境,Prod生產環境,這些環境下的一些配置肯定是不一樣的,比如數據庫配置,Redis配置,RabbitMQ配置。
如果每次切換發布環境,都需要修改配置重新構建的話,那對程序員來說將是噩夢,針對這種場景,Spring提供了@Profile注解來實現按照不同的環境裝配不同的bean,進而實現程序只需構建一次,但可以部署到多個環境。
2. 配置profile bean
為了更好的理解,我們通過具體的代碼示例來理解下Spring profile的使用方法,這里我們以數據庫配置為例。
說明:本篇博客的重點是講解@Profile注解的使用,數據庫的操作只是輔助理解@Profile,因此不會講解的太詳細,不過后續會單獨寫博客講解
假設我們有3套環境(Dev,QA,Prod),這3套環境的數據庫都使用的是mysql,但是其地址,用戶名,密碼都不一樣,那么在Java配置中,該如何聲明這些bean呢?
2.1 Java配置中配置profile bean
首先需要了解的是,@Profile
注解是從Spring 3.1版本中開始引入的,並且在這個版本中,@Profile
注解只能在類級別上使用。
因此我們可以按照環境分別創建數據庫配置,如下所示:
Dev環境下的數據庫配置:
package chapter03.profile;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import javax.sql.DataSource;
@Configuration
@Profile("dev")
public class DevDataSourceConfig {
@Bean
public DataSource devDataSource() {
System.out.println("This is dev DataSource");
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_action_db");
basicDataSource.setUsername("dev");
basicDataSource.setPassword("dev");
return basicDataSource;
}
}
使用上述代碼需要在pom.xml中添加如下依賴:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.7.0</version>
</dependency>
注意事項:如果類級別上使用了@Profile("dev"),那么該類中的所有bean都會在profile為dev時創建。
QA環境下的數據庫配置:
package chapter03.profile;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import javax.sql.DataSource;
@Configuration
@Profile("qa")
public class QADataSourceConfig {
@Bean
public DataSource qaDataSource() {
System.out.println("This is qa DataSource");
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3307/mybatis_action_db");
basicDataSource.setUsername("qa");
basicDataSource.setPassword("qa");
return basicDataSource;
}
}
Prod環境下的數據庫配置:
package chapter03.profile;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import javax.sql.DataSource;
@Configuration
@Profile("prod")
public class ProdDataSourceConfig {
@Bean
public DataSource prodDataSource() {
System.out.println("This is prod DataSource");
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3308/mybatis_action_db");
basicDataSource.setUsername("prod");
basicDataSource.setPassword("prod");
return basicDataSource;
}
}
不過從Spring 3.2開始,@Profile
注解可以與@Bean
注解一起在方法級別上使用。
這也就使得我們可以將剛剛的3個配置類合並成1個配置類(推薦該方式),如下所示:
package chapter03.profile;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
System.out.println("This is dev DataSource");
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_action_db");
basicDataSource.setUsername("dev");
basicDataSource.setPassword("dev");
return basicDataSource;
}
@Bean
@Profile("qa")
public DataSource qaDataSource() {
System.out.println("This is qa DataSource");
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3307/mybatis_action_db");
basicDataSource.setUsername("qa");
basicDataSource.setPassword("qa");
return basicDataSource;
}
@Bean
@Profile("prod")
public DataSource prodDataSource() {
System.out.println("This is prod DataSource");
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3308/mybatis_action_db");
basicDataSource.setUsername("prod");
basicDataSource.setPassword("prod");
return basicDataSource;
}
}
注意事項:沒有指定profile的bean始終都會創建,與激活哪個profile無關。
2.2 xml中配置profile bean
我們也可以通過<beans>
元素的profile屬性,在xml中配置profile bean,如下所示:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
profile="dev">
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mybatis_action_db"
p:username="dev"
p:password="dev"/>
</beans>
可以參考該配置,分別創建qa和prod環境的profile xml文件。
不過還是推薦使用嵌套的<beans>
元素,在一個xml文件中配置好3個環境的數據源,代碼如下所示:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans profile="dev">
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mybatis_action_db"
p:username="dev"
p:password="dev"/>
</beans>
<beans profile="qa">
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3307/mybatis_action_db"
p:username="qa"
p:password="qa"/>
</beans>
<beans profile="prod">
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3308/mybatis_action_db"
p:username="prod"
p:password="prod"/>
</beans>
</beans>
3. 激活profile
截止目前,我們按照環境的維度創建了3個bean,但實際運行時,只會創建1個bean,具體創建哪個bean取決於處於激活狀態的是哪個profile。
那么,我們該如何激活某個profile呢?
Spring在確定激活哪個profile時,需要依賴2個屬性:
- spring.profiles.active
- spring.profiles.default
spring.profiles.active
的優先級比spring.profiles.default
高,即如果沒有配置spring.profiles.active
,就使用spring.profiles.default
配置的值,如果配置了spring.profiles.active
,就不會再使用spring.profiles.default
配置的值。
如果兩者都沒有配置,就只會創建那些沒有定義profile的bean。
Web應用中,在web.xml中設置spring.profiles.active
的代碼如下所示:
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
也可以使用代碼方式激活:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("dev");
4. 單元測試
新建Main類,在其main()方法中添加如下測試代碼:
package chapter03.profile;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("dev");
context.register(DataSourceConfig.class);
context.refresh();
context.close();
}
}
輸出結果如下所示:
This is dev DataSource
如果將代碼修改為context.getEnvironment().setActiveProfiles("qa");
,輸出結果為:
This is qa DataSource
如果將代碼修改為context.getEnvironment().setActiveProfiles("prod");
,輸出結果為:
This is prod DataSource
5. 源碼及參考
源碼地址:https://github.com/zwwhnly/spring-action.git,歡迎下載。
汪雲飛《Java EE開發的顛覆者:Spring Boot實戰》
Craig Walls 《Spring實戰(第4版)》