第三十八章:基于SpringBoot架构使用Profile完成打包环境分离

在中大型企业项目开发中,环境分离是必不可少的一步,然而现在的开发人员也只是有这个概念,还是有很多项目采用普通的方式,每次打包发布部署的时候改动一大堆的配置文件,有一个地方忘记改就相当于白更新了一次系统,这种修改配置文件完成环境更换的方式给我们带来了很多的困扰,浪费了我们很多宝贵的时间!早在Spring 3.1版本就已经为我们提供了环境分离的相关注解配置方式,不过在传统的Spring项目中配置Profile确实有点麻烦,在Spring版本的不断更新直到后来SpringBoot成长起来后Profile已经能够很好支持项目配置环境分离。

免费教程专题

恒宇少年在博客整理三套免费学习教程专题,由于文章偏多特意添加了阅读指南,新文章以及之前的文章都会在专题内陆续填充,希望可以帮助大家解惑更多知识点。

本章目标

基于SpringBoot平台完成简单的数据库环境操作分离,根据激活不同的Profile完成不同的数据库操作。

SpringBoot 企业级核心技术学习专题


专题 专题名称 专题描述
001 Spring Boot 核心技术 讲解SpringBoot一些企业级层面的核心组件
002 Spring Boot 核心技术章节源码 Spring Boot 核心技术简书每一篇文章码云对应源码
003 Spring Cloud 核心技术 对Spring Cloud核心技术全面讲解
004 Spring Cloud 核心技术章节源码 Spring Cloud 核心技术简书每一篇文章对应源码
005 QueryDSL 核心技术 全面讲解QueryDSL核心技术以及基于SpringBoot整合SpringDataJPA
006 SpringDataJPA 核心技术 全面讲解SpringDataJPA核心技术
007 SpringBoot核心技术学习目录 SpringBoot系统的学习目录,敬请关注点赞!!!

构建项目

使用Idea工具创建一个SpringBoot项目,目前SpringBoot的版本已经更新至1.5.8,我们采用最新版本来完成本章内容,添加相关JPAMySQLDruidLombokWebFastJson等,pom.xml依赖相关配置如下所示:

....省略部分配置
<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>
....省略部分配置

配置数据库

我们创建三个数据库分别是project_prod => 线上环境数据库、project_dev=>开发环境数据库、project_beta=>线上测试环境数据库,这样我们在切换Profile时可以很好的区分环境,下面我们创建一张用户基本信息表,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分别在三个数据库内分别执行一次,保证我们数据结构环境一致,然后对应数据库分别插入数据,如下:

INSERT INTO `system_user_info` VALUES ('1', '线上测试环境用户', 'beta', 'beta_password');
INSERT INTO `system_user_info` VALUES ('1', '开发环境用户', 'dev', 'dev_password');
INSERT INTO `system_user_info` VALUES ('1', '正式环境用户', 'prod', 'prod_password');

这样我们就可以区分项目正在访问的具体环境。

创建Entity

对应system_user_info数据表创建一个数据实体,如下所示:

package com.yuqiyu.chapter38.entity;

import lombok.Data;

import javax.persistence.*;

/**
 * 用户基本信息实体
 * ========================
 * 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;
}

接下来我们为上面的实体创建一个JPA接口,继承JpaRepository<T,PK>接口完成Jpa扫描自动代理实例的动作。

创建JPA

SystemUserInfoJPA接口内容如下所示:

package com.yuqiyu.chapter38.jpa;

import com.yuqiyu.chapter38.entity.SystemUserInfoEntity;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * 系统用户信息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环境

SpringBoot内已经为了约定好了Profile配置文件的命名规则,即:application-xxx.properties或者application-xxx.yml,我们只需要将对应环境的配置文件放到resources目录下即可,也就是classpath下,我们对应我们的数据库环境编写三个不同的配置文件。

application-dev.yml

根据我们与SpringBoot的约定在application-dev.xml配置文件内配置的都是开发环境信息,里面包含了开发环境数据源配置信息,当然在实际的项目开发过程中配置信息可以任意约定。配置内容如下所示:

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
    #最大活跃数
    maxActive: 20
    #初始化数量
    initialSize: 1
    #最大连接等待超时时间
    maxWait: 60000
    #打开PSCache,并且指定每个连接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
    #配置监控统计拦截的filters,去掉后监控界面sql将无法统计,'wall'用于防火墙
    filters: stat, wall, log4j
  jpa:
    properties:
      hibernate:
        show_sql: true
        format_sql: true

在上面代码中可以看到,我们连接了本地的project_dev数据库来作为开发环境的访问数据源。

application-beta.yml

application-beta.yml配置文件就是我们与SpringBoot约定的线上测试环境,在我们实际的开发过程中线上测试环境肯定与开发环境不是同一个数据库,这时我们将application-dev.yml配置文件复制一份,修改下数据库链接信息即可,如果你的application-beta.yml还存在其他的配置,不要忘记修改成相关的环境配置。配置信息如下所示:

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
    #最大活跃数
    maxActive: 20
    #初始化数量
    initialSize: 1
    #最大连接等待超时时间
    maxWait: 60000
    #打开PSCache,并且指定每个连接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
    #配置监控统计拦截的filters,去掉后监控界面sql将无法统计,'wall'用于防火墙
    filters: stat, wall, log4j
  jpa:
    properties:
      hibernate:
        show_sql: true
        format_sql: true

application-prod.yml

application-prod.yml配置文件则是我们与SpringBoot约定的线上生产环境的配置文件,里面保存的全部都是正式环境配置信息,一般在开发过程中线上环境配置信息是不需要变动的,配置完成后就只是在打包部署时修改spring.profiles.activeprod就可以了(注:根据实际项目的线上环境的配置约定名称而定)。配置信息如下所示:

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
    #最大活跃数
    maxActive: 20
    #初始化数量
    initialSize: 1
    #最大连接等待超时时间
    maxWait: 60000
    #打开PSCache,并且指定每个连接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
    #配置监控统计拦截的filters,去掉后监控界面sql将无法统计,'wall'用于防火墙
    filters: stat, wall, log4j
  jpa:
    properties:
      hibernate:
        show_sql: true
        format_sql: true

为了方便我们测试,我在本地创建的三个数据库,当然实际项目开发中你可能是数据库读写分离环境,也可能是多台服务器完全分离的环境,只需要针对不同的约定修改相对应的配置信息就可以了。

测试Profile

下面我们来创建一个控制器,使用我们上面已经创建好的SystemUserInfoJPA完成数据库的读取动作。

创建测试控制器

在上面我们为每一个环境的数据库表````都初始化了一条数据,那么我就来编写一个读取数据库的请求方法,根据我们修改的spring.profiles.active配置文件内容,是否可以改变请求数据库。控制器代码如下所示:

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环境
 * ========================
 * 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);
    }
}

在控制器内,我们通过访问/user/{id}请求地址,就可以获取到用户的基本信息在页面上通过Json字符串的形式展示,下面我们就来配置需要激活的Profile,访问该请求地址查看输出效果。

激活Profile

由于激活Profile的配置不属于任何一个环境分离的配置文件,所以我们不可以在devbetaprod任意一个配置文件内添加激活配置,我们知道application.ymlSpringBoot约定的配置文件,那么我就在该配置文件内配置环境分离激活,配置如下所示:

spring:
  profiles:
    active: dev

我们在application.yml配置文件内激活了dev开发环境,下面我们启动项目访问请求路径http://127.0.0.1:8080/user/1来查看界面输出内容,如下所示:

{
  id: 1,
  nickName: "开发环境用户",
  loginName: "dev",
  loginPassword: "dev_password"
}

正如我们所料,正确的输出了开发环境的用户信息,那我们修改下激活环境是不是也会编程相对应的输出呢?下面我们来证实这一点,修改激活环境为线上开发环境:

spring:
  profiles:
    active: beta

重启项目,再次访问http://127.0.0.1:8080/user/1请求路径,界面输出内容如下所示:

{
  id: 1,
  nickName: "线上测试环境用户",
  loginName: "beta",
  loginPassword: "beta_password"
}

可以看到已经改成我们需要的效果,我们只是激活了不同的环境,就轻松实现了环境的分离,正式环境也是一样的,下面我们来激活正式环境完成Package打包。

正式环境打包

有很多项目在上线打包部署的时候需要改动很多配置文件,访问地址等等配置信息,那我们采用了Profile后打包该怎么处理呢?
答案是:省心。
第一步我们只需要修改激活环境改成线上环境即可,如下所示:

spring:
  profiles:
    active: prod

第二步运行打包命令,等待打包完成。本章是采用的Idea开发工具完成的打包,Idea工具为Maven自带了命令窗口,只需要选择不同的命令双击就可以执行,如下图1所示:

图1

我们双击package命令,等待打包完成就可以了,完成后jar或者war会在target目录生成,下面我们使用Windows CMD命令行进入jar存在的目录,执行命令之前需要关掉Idea启动的项目:

java -jar chapter38-0.0.1-SNAPSHOT.jar

启动完成后,我们再次访问请求地址http://127.0.0.1:8080/user/1,查看界面输出内容:

{
  id: 1,
  nickName: "正式环境用户",
  loginName: "prod",
  loginPassword: "prod_password"
}

正确输出了prod正式环境的用户信息。

总结

Profile的加入可以让很多运维实施人员减少了太多的烦恼,在几年前部署完全都是采用修改配置文件,如果修改出错还会导致返工,既浪费了时间也浪费了精力。

建议大家项目初期尽可能的采用环境分离的方式进行构建项目!

本章代码已经上传到码云:
SpringBoot配套源码地址:https://gitee.com/hengboy/spring-boot-chapter
SpringCloud配套源码地址:https://gitee.com/hengboy/spring-cloud-chapter

作者个人 博客
使用开源框架 ApiBoot 助你成为Api接口服务架构师

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容