前言
SpringBoot凭借完备的组件和友好性成为java领域的主流框架是不争的事实,随着微服务应用的越来越广相信docker大家也都不陌生,而采用windows作为开发平台的同学也不再少数。有过实际开发经验的人都应该遇到过windows(jar包),IDE(开发和单元测试),docker(部署)的配置问题,解决起来很让人头痛,这次我就借这篇文章的机会把个人的解决思路介绍一下,希望能够帮到大家少走一些弯路。
问题是什么
我们希望在如下三个环境下程序都能成功运行且得到一致的结果:
1.在Windwos下直接以Jar包的形式运行;
2.直接执行Docker化的容器;
3.直接在IDE下,执行开始和测试。
解决思路
在解决这个问题我们首先要理清困扰我们的到底是什么问题,即在不同的环境下要保证配置的一致性。同时保证可以在外部能够直接调整配置文件而不需要改代码。
环境分析
由于需要在外部能够直接调整配置,因此直接将配置放到代码里的做法就被pass了。
IDE开发和单元测试
直接在IDE中进行运行和执行单元测试。
windows的Jar包运行
实际上就是在Windows下单独运行的Jar包
Docker运行
实际上就是Linux下单独运行的Jar包
总结一下:
我们的配置要兼容Linux和Windows系统,我们的配置需要支持在IDE下获得跟文件系统下相同的路径。
好了要想得到上面这样的效果,我们需要能够兼容不同的操作系统下得到相同的路径和文件。
基本原理
配置优先级
Spring Boot 所提供的配置优先级顺序比较复杂。按照优先级从高到低的顺序,具体的列表如下所示。
1. 命令行参数。
2. 通过 System.getProperties() 获取的参数。
3. 操作系统环境变量。
4. 从 java:comp/env 得到的 JNDI 属性。
5. 通过 RandomValuePropertySource 生成的“random.*”属性。
6. 应用 Jar 文件之外的属性文件。(通过spring.config.location参数)
7. 应用 Jar 文件内部的属性文件。
8. 在应用配置 Java 类(包含“@Configuration”注解的 Java类)中通过“@PropertySource”注解声明的属性文件。
9. 通过“SpringApplication.setDefaultProperties”声明的默认属性。
SpringBoot的目录问题
我们直接来讨论;System.getProperty("user.dir")这条指令的作用。
1.IDE下,返回工程的主路径也就是跟pom.xml同级路径
2.在windows下会返回jar包同级路径
3.在linux下也会返回jar包同级路径下
也就意味着我们可以通过调整将配置文件在不同环境的不同位置就可以保证系统的配置一致性。
基本路线
1.在工程下建立单独的配置路径
2.在工程中利用代码获取配置路径并读取配置项/
3.利用maven的资源插件在构建时自动将配置文件拷贝到构建目录下
4.利用Docker插件将所需文件复制到构建Docker的资源文件夹下
5.利用DockerFile将配置文件打入到容器中
实例工程
目录结构
依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>copy-conf</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<outputDirectory>${project.build.directory}/ext/conf</outputDirectory>
<resources>
<resource>
<directory>ext/conf</directory>
<includes>
<include>demoConf.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.5.201505241946</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-prepare-agent-integration</id>
<goals>
<goal>prepare-agent-integration</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- tag::plugin[] -->
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.3</version>
<configuration>
<!--imagename不能使用大写字母或任何特殊字符-->
<imageName>demo_server</imageName>
<dockerDirectory>src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
<include>ext/conf/demoConf.xml</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
</project>
读取配置文件
package com.example.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
@SpringBootApplication
public class DemoApplication {
private final Logger logger= LoggerFactory.getLogger(DemoApplication.class);
public DemoApplication() {
String configFilePath = System.getProperty("user.dir") + "\\ext\\conf\\demoConf.xml";
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
File file = new File(configFilePath);
Document doc = builder.parse(file);
Element root = doc.getDocumentElement();
Node node;
node = root.getElementsByTagName("key").item(0);
String value = node.getAttributes().getNamedItem("value").getNodeValue();
} catch (Exception e) {
logger.error("读取自身配置文件错误",e);
}
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
DockerFile
FROM waylau/docker-spring-boot
VOLUME /tmp
ADD demo-0.0.1-SNAPSHOT.jar app.jar
ADD ext/conf/hyConfig.xml ext/conf/demoConf.xml
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
GitHub地址:
github.com/zhaoenweiex…
总结
1.有些事情看起来很简单但是解决起来非常困难,不能眼高手低
2.要解决实际问题有时候要透彻理解一些概念,比如这里的SpringBoot的配置优先级,SpringBoot的路径问题
3.Windwos下依靠安装toolkit部署Docker的同学要注意了:
在执行mvn clean instll docker:build时你们会执行到最后一步报错,这时候要采用docker客户端切换到工程目录下直接执行docker build命令就可以完成打包