1.情景展示
声明:本文和springboot没有关系,本质是:maven与spring相互作用产生的效果,之所以标题使用它,是为了让更多人看到。
在实际开发过程中,我们经常会有这样需求:
开发环境或测试环境,使用测试数据库;生产环境使用正式数据库。
日志级别、引用的jar包、打包方式有时也会不一样,这样,同一项目就会存在多个运行环境。
对于初学者而言,通常的做法就是:
开发的时候在配置文件使用相关配置信息;需要部署项目的时候,再将原有代码注释掉,重新配置一套环境;打完包,在本地调试的时候,再将生产环境配置注销掉,还原开发环境。
首先,这种方法肯定是可行的,但经常改来改去,不便维护及拓展。
下面,介绍一种更好的方案。
2.环境分析
我们先来了解一下,环境的种类有哪些?
- 开发环境:development,通常用dev表示;
- 测试环境:test,通常使用test表示;
- 预演环境:preview,通常使用prev表示,相当于试运行阶段,处于测试和正式阶段之间;
- 生产环境:production,通常使用prod表示。
基本上上面的4种环境,就涵盖了我们研发一种产品的所有阶段。
这里,需要说明的是:环境的名称是可以自定义的,你可以定义成任何名称,只不过是上面4种是大家约定俗称的名字而已,无论是谁看到,就能立马明白什么意思;而如果你将名字定义成aa,也许只有你自己知道它代表的是哪个环境了。
下面,我先讲一种大众化的多环境开发模式。
第一,application.properties/yml,作为spring的主配置文件。
由该配置文件来决定,哪个配置文件生效。
通过spring.profiles.active来设置生效的配置文件,如上图所示,我使用的是dev,在启动项目时,spring会加载application-dev.properties
第二,设置环境配置文件
这种方式,很简单,容易上手,没什么好说的。
3.解决方案
这里,介绍一种更为高级的使用方式。
通过pom.xml的profile标签来管理环境,换句话说就是:使用maven来完成环境的管理,在使用maven命令进行打包时实现。
打包形式、打包时是否跳过测试阶段、是否启用接口说明文档、日志级别、以及引用的jar包,通通由profile来管理,实现在多环境中共存。
先来看看pom.xml的构成吧,关键代码展示:
<groupId>com.公司简称</groupId> <artifactId>项目名称</artifactId> <!--打包形式:通过maven的profile来决定打成war包还是打jar包(如果不配置packaging标签的话,默认值是jar)--> <packaging>${project.packaging}</packaging> <version>0.0.1-SNAPSHOT</version> <name>项目名称</name> <description>项目简述</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.1.RELEASE</version> </parent> <properties> <!--指定tomcat内置版本(只对springboot内置tomcat生效)--> <!--<tomcat.version>8.5.0</tomcat.version>--> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <!--java版本--> <java.version>1.8</java.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <!--proguard版本--> <proguard.version>6.2.2</proguard.version> <!--spring版本号--> <spring.version>2.3.1.RELEASE</spring.version> <!--打包时是否跳过测试阶段(使用profile来指定打包时是否进行测试)--> <skipTests>${skipTests}</skipTests> <!--加载application-*.yml配置文件(通过该标签来指定即将生效的配置文件)--> <!--加载application-test.yml配置文件(profile需要勾选成test)--> <spring.profiles.active>test</spring.profiles.active> <!--基层(测试环境)--> <!--<spring.profiles.active>jc</spring.profiles.active>--> <!--加载application-prod.yml配置文件--> <!--这样,这里就可以根据实际需要,进行多个生产环境间的切换(profile需要勾选成prod)--> <!--基层(正式环境)--> <!--<spring.profiles.active>jc</spring.profiles.active>--> </properties> <!--根据不同的环境引用不同的jar包,最终统一打包到项目当中--> <!--生产环境:prod,开发环境:dev,测试环境:test,预演环境:prev 使用maven命令打包介绍: 开发环境打包:mvn clean package -Dmaven.test.skip=true -P dev 测试环境打包:mvn clean package -Dmaven.test.skip=true -P test 生产环境打包:mvn clean package -Dmaven.test.skip=true -P prod !!!另外,在idea中切换生产环境和开发环境时,需要重新导包!!!--> <profiles> <!--开发环境--> <profile> <id>dev</id> <activation> <!-- 默认激活本环境 --> <activeByDefault>true</activeByDefault> </activation> <properties> <!--加载application-dev.yml配置文件(profile需要勾选成dev)--> <spring.profiles.active>dev</spring.profiles.active> <!--environment这个节点是我自己取的:yml文件根据该标签的值来确定接口地址是正式地址还是测试地址--> <environment>development</environment> <!--是否是生产环境:通过配置该值,来决定是否启用knife4j--> <isProduction>false</isProduction> <!--日志级别--> <logLevel>DEBUG</logLevel> <!--打包方式: 设置成jar包时,在idea中,不能通过插件的package进行打包, 只能通过命令来实现:mvn clean package -Dmaven.test.skip=true -P dev --> <project.packaging>jar</project.packaging> <!--打包时,需要进行测试--> <skipTests>true</skipTests> </properties> <dependencies> <!--jsp不能够在jar中使用,只能够在War中使用 所以,如果确定部署项目的时候以jar的形式运行的话,则项目就不能使用jsp了, 因为,maven在执行打包命令时,jsp是不会被打包到jar包当中的--> <!-- 使用jsp引擎,springboot内置tomcat没有此依赖 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>9.0.36</version> </dependency> <!--增加对 JSP 文件的支持--> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jsp-api</artifactId> <version>9.0.36</version> </dependency> <!-- 添加jstl标签库依赖模块 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> </profile> <!--测试环境--> <profile> <id>test</id> <activation> <activeByDefault>false</activeByDefault> </activation> <properties> <environment>test</environment> <isProduction>false</isProduction> <logLevel>INFO</logLevel> <project.packaging>war</project.packaging> <skipTests>true</skipTests> </properties> <dependencies> <!--引用的jar包与生产环境一样,这里不再展示--> </dependencies> </profile> <!--预演环境--> <profile> <id>prev</id> <activation> <activeByDefault>false</activeByDefault> </activation> <properties> <environment>preview</environment> <isProduction>true</isProduction> <logLevel>INFO</logLevel> <project.packaging>war</project.packaging> <skipTests>true</skipTests> </properties> <dependencies> <!--引用的jar包与生产环境一样,这里不再展示--> </dependencies> </profile> <!--生产环境--> <!--在本地通过Application启动项目时,其本质还是使用的springboot的内置tomcat,由于内置tomcat不支持使用jsp, 所以,此时是无法访问项目对应的jsp页面的, 只有将其部署在tomcat上并启动SpringBootStartApplication才能正常访问--> <profile> <id>prod</id> <!-- 是否激活本环境 --> <activation> <activeByDefault>false</activeByDefault> </activation> <properties> <environment>production</environment> <isProduction>true</isProduction> <logLevel>ERROR</logLevel> <project.packaging>war</project.packaging> <!--打包时,跳过测试阶段(因为测试阶段会去连接数据库,正式数据库本地无法访问,会导致打包失败)--> <skipTests>true</skipTests> </properties> <!--项目中,编译和测试阶段用到的jar包,但tomcat中存在这些jar包,此时,在部署到tomcat中时,我们就需要把它们踢掉--> <dependencies> <!--内置tomcat(剔除该jar包)--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <!--只有编译和测试阶段生效--> <scope>provided</scope> </dependency> <!-- servlet依赖(只在开发时使用,因为部署到tomcat上时,tomcat有对应的jar包) --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency> <!-- jstl标签库依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> <scope>provided</scope> </dependency> </dependencies> </profile> </profiles> <!--配置项目的jar包仓库--> <repositories> <!--阿里云仓库--> <repository> <id>central</id> <name>central maven</name> <url>https://maven.aliyun.com/repository/central</url> <!--<url>http://maven.aliyun.com/nexus/content/groups/public/</url>--> </repository> <!--maven官网--> <repository> <id>public</id> <name>public maven</name> <url>https://mvnrepository.com</url> </repository> </repositories> <!--jar包依赖--> <dependencies> <!--公共类封装引用--> <dependency> <groupId>code.marydon.encapsulation</groupId> <artifactId>javaUtils</artifactId> <version>1.0</version> </dependency> </dependencies>
再来看看application.yml主配置文件
这里的关键点在于:
yml文件想要引用pom.xml中的property标签的值时,在要引用的标签名称两边加上@,即:@propertyName@;如果是properties文件想要引用,使用的是EL表达式,${propertyName}。
这样,就将启用的配置文件的决定权交给了pom.xml。
提示:
在idea中,按住Ctrl键不松手,会跳转到对应pom.xml该标签所处位置。
knife4j配置(如果没有,就忽略)
实现的效果就是:开发环境和测试环境可以访问接口文档,预演环境和生产环境禁止访问。
数据库配置、日志配置等自定义配置不在主配置文件里放,放到对应的环境配置文件当中。
假设,我们需要调用第三方的接口,而第三方接口也分测试地址和正式地址。
那我们就可以在这里使用自定义标签,把正式接口和测试接口添加到配置文件当中。
如果没有这种需求,就可以跳过。
这样,我们分别获取到当前生效的是哪种环境,测试接口地址,正式接口地址,根据环境来决定最终调用哪个地址。
这只是环境的一种使用方式,还有一种应用场景是:根据环境来控制控制层的是否可见(特定请求只在特定环境生效),下篇文章会讲。
最后,来看看日志。
日志级别,也由pom.xml的profile标签来决定。
通常情况下,开发环境使用debug、测试环境使用info,生产环境使用error。
每种环境的个性化需求,都可以通过这种方式来实现。
如何正确使用多环境的切换?
在idea中,想要项目完成环境的切换,至少需要完成前三步,一般项目在清空target目录后,idea会完成自动编译,如果没有那就是你没有设置自动编译;
在idea中,经常会出现因idea自带的maven插件因环境切换失败导致项目编译失败的问题,这也没有办法,是idea自身的问题,重复上述步骤即可。
原理就是:maven插件会将yml文件中引用的标签的值写入对应的配置文件中。
补充一点:
如上图所示,一个地方会产生一个配置文件,而每个地方又可分为生产环境和测试环境,这时候,第一种方式就会显得力不从心。
现在,我们只需要三步即可:
增加一个配置文件,比如叫做:application-aa.yml;
在pom.xml中,将原有的<spring.profiles.active>注释掉,添加一个同样的标签<spring.profiles.active>aa</spring.profiles.active>;
选择要生效的环境,重新编译项目即可。
这样,就实现了多区域多环境可以随意切换的效果。