在中大型企業(yè)項(xiàng)目開發(fā)中,環(huán)境分離是必不可少的一步,然而現(xiàn)在的開發(fā)人員也只是有這個(gè)概念,還是有很多項(xiàng)目采用普通的方式,每次打包發(fā)布部署的時(shí)候改動(dòng)一大堆的配置文件,有一個(gè)地方忘記改就相當(dāng)于白更新了一次系統(tǒng),這種修改配置文件完成環(huán)境更換的方式給我們帶來了很多的困擾,浪費(fèi)了我們很多寶貴的時(shí)間!早在Spring 3.1版本就已經(jīng)為我們提供了環(huán)境分離的相關(guān)注解配置方式,不過在傳統(tǒng)的Spring項(xiàng)目中配置Profile確實(shí)有點(diǎn)麻煩,在Spring版本的不斷更新直到后來SpringBoot成長起來后Profile已經(jīng)能夠很好支持項(xiàng)目配置環(huán)境分離。
免費(fèi)教程專題
恒宇少年在博客整理三套免費(fèi)學(xué)習(xí)教程專題,由于文章偏多特意添加了閱讀指南,新文章以及之前的文章都會(huì)在專題內(nèi)陸續(xù)填充,希望可以幫助大家解惑更多知識(shí)點(diǎn)。
本章目標(biāo)
基于SpringBoot平臺(tái)完成簡單的數(shù)據(jù)庫環(huán)境操作分離,根據(jù)激活不同的Profile完成不同的數(shù)據(jù)庫操作。
SpringBoot 企業(yè)級核心技術(shù)學(xué)習(xí)專題
| 專題 | 專題名稱 | 專題描述 |
|---|---|---|
| 001 | Spring Boot 核心技術(shù) | 講解SpringBoot一些企業(yè)級層面的核心組件 |
| 002 | Spring Boot 核心技術(shù)章節(jié)源碼 | Spring Boot 核心技術(shù)簡書每一篇文章碼云對應(yīng)源碼 |
| 003 | Spring Cloud 核心技術(shù) | 對Spring Cloud核心技術(shù)全面講解 |
| 004 | Spring Cloud 核心技術(shù)章節(jié)源碼 | Spring Cloud 核心技術(shù)簡書每一篇文章對應(yīng)源碼 |
| 005 | QueryDSL 核心技術(shù) | 全面講解QueryDSL核心技術(shù)以及基于SpringBoot整合SpringDataJPA |
| 006 | SpringDataJPA 核心技術(shù) | 全面講解SpringDataJPA核心技術(shù) |
| 007 | SpringBoot核心技術(shù)學(xué)習(xí)目錄 | SpringBoot系統(tǒng)的學(xué)習(xí)目錄,敬請關(guān)注點(diǎn)贊??!! |
構(gòu)建項(xiàng)目
使用Idea工具創(chuàng)建一個(gè)SpringBoot項(xiàng)目,目前SpringBoot的版本已經(jīng)更新至1.5.8,我們采用最新版本來完成本章內(nèi)容,添加相關(guān)JPA、MySQL、Druid、Lombok、Web、FastJson等,pom.xml依賴相關(guān)配置如下所示:
....省略部分配置
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入druid最新maven依賴-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.39</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
....省略部分配置
配置數(shù)據(jù)庫
我們創(chuàng)建三個(gè)數(shù)據(jù)庫分別是project_prod => 線上環(huán)境數(shù)據(jù)庫、project_dev=>開發(fā)環(huán)境數(shù)據(jù)庫、project_beta=>線上測試環(huán)境數(shù)據(jù)庫,這樣我們在切換Profile時(shí)可以很好的區(qū)分環(huán)境,下面我們創(chuàng)建一張用戶基本信息表,SQL如下:
-- ----------------------------
-- Table structure for system_user_info
-- ----------------------------
DROP TABLE IF EXISTS `system_user_info`;
CREATE TABLE `system_user_info` (
`SUI_ID` int(11) NOT NULL AUTO_INCREMENT,
`SUI_NICK_NAME` varchar(50) DEFAULT NULL,
`SUI_LOGIN_NAME` varchar(30) DEFAULT NULL,
`SUI_LOGIN_PASSWORD` varchar(32) DEFAULT NULL,
PRIMARY KEY (`SUI_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
將上面SQL分別在三個(gè)數(shù)據(jù)庫內(nèi)分別執(zhí)行一次,保證我們數(shù)據(jù)結(jié)構(gòu)環(huán)境一致,然后對應(yīng)數(shù)據(jù)庫分別插入數(shù)據(jù),如下:
INSERT INTO `system_user_info` VALUES ('1', '線上測試環(huán)境用戶', 'beta', 'beta_password');
INSERT INTO `system_user_info` VALUES ('1', '開發(fā)環(huán)境用戶', 'dev', 'dev_password');
INSERT INTO `system_user_info` VALUES ('1', '正式環(huán)境用戶', 'prod', 'prod_password');
這樣我們就可以區(qū)分項(xiàng)目正在訪問的具體環(huán)境。
創(chuàng)建Entity
對應(yīng)system_user_info數(shù)據(jù)表創(chuàng)建一個(gè)數(shù)據(jù)實(shí)體,如下所示:
package com.yuqiyu.chapter38.entity;
import lombok.Data;
import javax.persistence.*;
/**
* 用戶基本信息實(shí)體
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2017/10/29
* Time:08:25
* 碼云:http://git.oschina.net/jnyqy
* ========================
*/
@Entity
@Table(name = "system_user_info")
@Data
public class SystemUserInfoEntity
{
/**
* 主鍵
*/
@Column(name = "SUI_ID")
@GeneratedValue
@Id
private Integer id;
/**
* 昵稱
*/
@Column(name = "SUI_NICK_NAME")
private String nickName;
/**
* 登錄名
*/
@Column(name = "SUI_LOGIN_NAME")
private String loginName;
/**
* 登錄密碼
*/
@Column(name = "SUI_LOGIN_PASSWORD")
private String loginPassword;
}
接下來我們?yōu)樯厦娴膶?shí)體創(chuàng)建一個(gè)JPA接口,繼承JpaRepository<T,PK>接口完成Jpa掃描自動(dòng)代理實(shí)例的動(dòng)作。
創(chuàng)建JPA
SystemUserInfoJPA接口內(nèi)容如下所示:
package com.yuqiyu.chapter38.jpa;
import com.yuqiyu.chapter38.entity.SystemUserInfoEntity;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* 系統(tǒng)用戶信息jpa
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2017/10/29
* Time:08:30
* 碼云:http://git.oschina.net/jnyqy
* ========================
*/
public interface SystemUserInfoJPA
extends JpaRepository<SystemUserInfoEntity,Integer>
{
}
配置Profile環(huán)境
在SpringBoot內(nèi)已經(jīng)為了約定好了Profile配置文件的命名規(guī)則,即:application-xxx.properties或者application-xxx.yml,我們只需要將對應(yīng)環(huán)境的配置文件放到resources目錄下即可,也就是classpath下,我們對應(yīng)我們的數(shù)據(jù)庫環(huán)境編寫三個(gè)不同的配置文件。
application-dev.yml
根據(jù)我們與SpringBoot的約定在application-dev.xml配置文件內(nèi)配置的都是開發(fā)環(huán)境信息,里面包含了開發(fā)環(huán)境數(shù)據(jù)源配置信息,當(dāng)然在實(shí)際的項(xiàng)目開發(fā)過程中配置信息可以任意約定。配置內(nèi)容如下所示:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/project_dev?characterEncoding=utf8
username: root
password: 123456
#最大活躍數(shù)
maxActive: 20
#初始化數(shù)量
initialSize: 1
#最大連接等待超時(shí)時(shí)間
maxWait: 60000
#打開PSCache,并且指定每個(gè)連接PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
#通過connectionProperties屬性來打開mergeSql功能;慢SQL記錄
#connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 1 from dual
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
#配置監(jiān)控統(tǒng)計(jì)攔截的filters,去掉后監(jiān)控界面sql將無法統(tǒng)計(jì),'wall'用于防火墻
filters: stat, wall, log4j
jpa:
properties:
hibernate:
show_sql: true
format_sql: true
在上面代碼中可以看到,我們連接了本地的project_dev數(shù)據(jù)庫來作為開發(fā)環(huán)境的訪問數(shù)據(jù)源。
application-beta.yml
application-beta.yml配置文件就是我們與SpringBoot約定的線上測試環(huán)境,在我們實(shí)際的開發(fā)過程中線上測試環(huán)境肯定與開發(fā)環(huán)境不是同一個(gè)數(shù)據(jù)庫,這時(shí)我們將application-dev.yml配置文件復(fù)制一份,修改下數(shù)據(jù)庫鏈接信息即可,如果你的application-beta.yml還存在其他的配置,不要忘記修改成相關(guān)的環(huán)境配置。配置信息如下所示:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/project_beta?characterEncoding=utf8
username: root
password: 123456
#最大活躍數(shù)
maxActive: 20
#初始化數(shù)量
initialSize: 1
#最大連接等待超時(shí)時(shí)間
maxWait: 60000
#打開PSCache,并且指定每個(gè)連接PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
#通過connectionProperties屬性來打開mergeSql功能;慢SQL記錄
#connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 1 from dual
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
#配置監(jiān)控統(tǒng)計(jì)攔截的filters,去掉后監(jiān)控界面sql將無法統(tǒng)計(jì),'wall'用于防火墻
filters: stat, wall, log4j
jpa:
properties:
hibernate:
show_sql: true
format_sql: true
application-prod.yml
而application-prod.yml配置文件則是我們與SpringBoot約定的線上生產(chǎn)環(huán)境的配置文件,里面保存的全部都是正式環(huán)境配置信息,一般在開發(fā)過程中線上環(huán)境配置信息是不需要變動(dòng)的,配置完成后就只是在打包部署時(shí)修改spring.profiles.active為prod就可以了(注:根據(jù)實(shí)際項(xiàng)目的線上環(huán)境的配置約定名稱而定)。配置信息如下所示:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/project_prod?characterEncoding=utf8
username: root
password: 123456
#最大活躍數(shù)
maxActive: 20
#初始化數(shù)量
initialSize: 1
#最大連接等待超時(shí)時(shí)間
maxWait: 60000
#打開PSCache,并且指定每個(gè)連接PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
#通過connectionProperties屬性來打開mergeSql功能;慢SQL記錄
#connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 1 from dual
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
#配置監(jiān)控統(tǒng)計(jì)攔截的filters,去掉后監(jiān)控界面sql將無法統(tǒng)計(jì),'wall'用于防火墻
filters: stat, wall, log4j
jpa:
properties:
hibernate:
show_sql: true
format_sql: true
為了方便我們測試,我在本地創(chuàng)建的三個(gè)數(shù)據(jù)庫,當(dāng)然實(shí)際項(xiàng)目開發(fā)中你可能是數(shù)據(jù)庫讀寫分離環(huán)境,也可能是多臺(tái)服務(wù)器完全分離的環(huán)境,只需要針對不同的約定修改相對應(yīng)的配置信息就可以了。
測試Profile
下面我們來創(chuàng)建一個(gè)控制器,使用我們上面已經(jīng)創(chuàng)建好的SystemUserInfoJPA完成數(shù)據(jù)庫的讀取動(dòng)作。
創(chuàng)建測試控制器
在上面我們?yōu)槊恳粋€(gè)環(huán)境的數(shù)據(jù)庫表````都初始化了一條數(shù)據(jù),那么我就來編寫一個(gè)讀取數(shù)據(jù)庫的請求方法,根據(jù)我們修改的spring.profiles.active配置文件內(nèi)容,是否可以改變請求數(shù)據(jù)庫。控制器代碼如下所示:
package com.yuqiyu.chapter38;
import com.yuqiyu.chapter38.entity.SystemUserInfoEntity;
import com.yuqiyu.chapter38.jpa.SystemUserInfoJPA;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 測試profile環(huán)境
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2017/10/29
* Time:09:02
* 碼云:http://git.oschina.net/jnyqy
* ========================
* @author hengyu
*/
@RestController
@RequestMapping(value = "/user")
public class IndexController
{
@Autowired
private SystemUserInfoJPA systemUserInfoJPA;
/**
* 查詢用戶詳情
* @param id
* @return
*/
@RequestMapping(value = "/{id}")
public SystemUserInfoEntity detail(@PathVariable("id") Integer id)
throws Exception
{
return systemUserInfoJPA.findOne(id);
}
}
在控制器內(nèi),我們通過訪問/user/{id}請求地址,就可以獲取到用戶的基本信息在頁面上通過Json字符串的形式展示,下面我們就來配置需要激活的Profile,訪問該請求地址查看輸出效果。
激活Profile
由于激活Profile的配置不屬于任何一個(gè)環(huán)境分離的配置文件,所以我們不可以在dev、beta、prod任意一個(gè)配置文件內(nèi)添加激活配置,我們知道application.yml是SpringBoot約定的配置文件,那么我就在該配置文件內(nèi)配置環(huán)境分離激活,配置如下所示:
spring:
profiles:
active: dev
我們在application.yml配置文件內(nèi)激活了dev開發(fā)環(huán)境,下面我們啟動(dòng)項(xiàng)目訪問請求路徑http://127.0.0.1:8080/user/1來查看界面輸出內(nèi)容,如下所示:
{
id: 1,
nickName: "開發(fā)環(huán)境用戶",
loginName: "dev",
loginPassword: "dev_password"
}
正如我們所料,正確的輸出了開發(fā)環(huán)境的用戶信息,那我們修改下激活環(huán)境是不是也會(huì)編程相對應(yīng)的輸出呢?下面我們來證實(shí)這一點(diǎn),修改激活環(huán)境為線上開發(fā)環(huán)境:
spring:
profiles:
active: beta
重啟項(xiàng)目,再次訪問http://127.0.0.1:8080/user/1請求路徑,界面輸出內(nèi)容如下所示:
{
id: 1,
nickName: "線上測試環(huán)境用戶",
loginName: "beta",
loginPassword: "beta_password"
}
可以看到已經(jīng)改成我們需要的效果,我們只是激活了不同的環(huán)境,就輕松實(shí)現(xiàn)了環(huán)境的分離,正式環(huán)境也是一樣的,下面我們來激活正式環(huán)境完成Package打包。
正式環(huán)境打包
有很多項(xiàng)目在上線打包部署的時(shí)候需要改動(dòng)很多配置文件,訪問地址等等配置信息,那我們采用了Profile后打包該怎么處理呢?
答案是:省心。
第一步我們只需要修改激活環(huán)境改成線上環(huán)境即可,如下所示:
spring:
profiles:
active: prod
第二步運(yùn)行打包命令,等待打包完成。本章是采用的Idea開發(fā)工具完成的打包,Idea工具為Maven自帶了命令窗口,只需要選擇不同的命令雙擊就可以執(zhí)行,如下圖1所示:

我們雙擊
package命令,等待打包完成就可以了,完成后jar或者war會(huì)在target目錄生成,下面我們使用Windows CMD命令行進(jìn)入jar存在的目錄,執(zhí)行命令之前需要關(guān)掉Idea啟動(dòng)的項(xiàng)目:
java -jar chapter38-0.0.1-SNAPSHOT.jar
啟動(dòng)完成后,我們再次訪問請求地址http://127.0.0.1:8080/user/1,查看界面輸出內(nèi)容:
{
id: 1,
nickName: "正式環(huán)境用戶",
loginName: "prod",
loginPassword: "prod_password"
}
正確輸出了prod正式環(huán)境的用戶信息。
總結(jié)
Profile的加入可以讓很多運(yùn)維實(shí)施人員減少了太多的煩惱,在幾年前部署完全都是采用修改配置文件,如果修改出錯(cuò)還會(huì)導(dǎo)致返工,既浪費(fèi)了時(shí)間也浪費(fèi)了精力。
建議大家項(xiàng)目初期盡可能的采用環(huán)境分離的方式進(jìn)行構(gòu)建項(xiàng)目!
本章代碼已經(jīng)上傳到碼云:
SpringBoot配套源碼地址:https://gitee.com/hengboy/spring-boot-chapter
SpringCloud配套源碼地址:https://gitee.com/hengboy/spring-cloud-chapter