这个接着前一个多个服务合并为一个服务的文章,详解yml配置文件合并的问题
由于每个微服务都有yml配置文件,如果合并打包的话,前一篇文章也说了,相同文件会进行覆盖,所以需要自己实现一个适配器,进行yml文件的合并。使用maven-assembly-plugin进行多文件合并逻辑的实现。
首先新建一个模块,实现yml合并的逻辑。
1、pom文件
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.5.RELEASE</version> <relativePath/> </parent> <groupId>com.fql.merge</groupId> <artifactId>preHandle</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.apache.maven.plugin-tools</groupId> <artifactId>maven-plugin-annotations</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.27</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-component-metadata</artifactId> <version>2.2.0</version> <executions> <execution> <goals> <goal>generate-metadata</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project> |
合并类实现
package com.lfq.integ.util;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import org.apache.maven.plugins.assembly.filter.ContainerDescriptorHandler;
import org.apache.maven.plugins.assembly.utils.AssemblyFileUtils;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.components.io.fileselectors.FileInfo;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.codehaus.plexus.util.IOUtil;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
/**
* @author lifengqin
*/
@Component(role = ContainerDescriptorHandler.class, hint = "yml-merge", instantiationStrategy = "per-lookup" )
public class MySimpleAggregatingDescriptorHandler implements ContainerDescriptorHandler, LogEnabled{
@SuppressWarnings( "FieldCanBeLocal" )
private final String commentChars = "#";
private final StringWriter aggregateWriter = new StringWriter();
private final List<String> filenames = new ArrayList<>();
private String filePattern;
private String outputPath;
private boolean overrideFilterAction;
private Logger logger;
private Archiver archiver;
private File temp;
@Override
public void finalizeArchiveCreation(Archiver archiver )
{
checkConfig();
if ( this.outputPath.endsWith( "/" ) ) {
throw new ArchiverException( "Cannot write aggregated properties to a directory. "
+ "You must specify a file name in the outputPath configuration for this"
+ " handler. (handler: " + getClass().getName() );
}
if ( this.outputPath.startsWith( "/" ) ) {
this.outputPath = this.outputPath.substring( 1 );
}
this.temp = this.writePropertiesFile();
this.overrideFilterAction = true;
archiver.addFile(this.temp, this.outputPath);
this.archiver = archiver;
this.overrideFilterAction = false;
}
private File writePropertiesFile() {
File f;
Writer writer = null;
try {
f = File.createTempFile("maven-assembly-plugin", "tmp");
f.deleteOnExit();
writer = AssemblyFileUtils.isPropertyFile( f )
? new OutputStreamWriter( new FileOutputStream( f , true), StandardCharsets.ISO_8859_1 )
: new OutputStreamWriter( new FileOutputStream( f , true) );
writer.write( "\n\n" );
writer.write( aggregateWriter.toString() );
writer.close();
writer = null;
}
catch ( final IOException e ) {
throw new ArchiverException(
"Error adding aggregated properties to finalize archive creation. Reason: " + e.getMessage(), e );
}
finally {
IOUtil.close(writer);
}
return f;
}
@Override
public void finalizeArchiveExtraction( final UnArchiver unarchiver ) {
}
@Override
public List<String> getVirtualFiles() {
checkConfig();
return Collections.singletonList( outputPath );
}
@Override
public boolean isSelected(final FileInfo fileInfo ) throws IOException {
if (fileInfo == null){
throw new IllegalStateException("fileInfo not be none");
}
checkConfig();
if ( overrideFilterAction ) {
return true;
}
String name = AssemblyFileUtils.normalizeFileInfo( fileInfo );
if ( fileInfo.isFile() && name.matches( filePattern ) ) {
String content = readProperties( fileInfo );
if (name.matches(".*/application.yml")) {
writeFile(content);
}
return false;
}
return true;
}
private void checkConfig() {
if ( filePattern == null || outputPath == null ) {
throw new IllegalStateException(
"You must configure filePattern and outputPath in your containerDescriptorHandler declaration." );
}
}
/**
*读取文件
* @param fileInfo
* @throws IOException
*/
private String readProperties( final FileInfo fileInfo ) throws IOException {
try (StringWriter writer = new StringWriter();
Reader reader = AssemblyFileUtils.isPropertyFile(fileInfo.getName())
? new InputStreamReader(fileInfo.getContents(), StandardCharsets.ISO_8859_1)
: new InputStreamReader(fileInfo.getContents())) {
IOUtil.copy(reader, writer);
final String content = writer.toString();
aggregateWriter.write("\n");
aggregateWriter.write(content);
return content;
}
}
//输出合并后的yml文件
private void writeFile(String content) {
Writer writer = null;
try {
writer = AssemblyFileUtils.isPropertyFile( this.temp )
? new OutputStreamWriter( new FileOutputStream( this.temp , false), StandardCharsets.ISO_8859_1 )
: new OutputStreamWriter( new FileOutputStream( this.temp, false ) );
//去掉{}样式
DumperOptions options = new DumperOptions();
options.setPrettyFlow(true);
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
// 创建YAML对象
Yaml yam = new Yaml(options);
Map<Object, Object> mergeYmlMap = new HashMap<>();
// 将合并后的YAML文件加载到Map对象中
for (Object o : yam.loadAll(String.valueOf(this.aggregateWriter))) {
Map<Object, Object> ymlMap = (Map<Object, Object>) o;
for (Map.Entry<Object, Object> entry : ymlMap.entrySet()) {
if (!mergeYmlMap.containsKey(entry.getKey())) {
mergeYmlMap.put(entry.getKey(), entry.getValue());
} else if (entry.getValue() instanceof Map){
mergeMaps((Map<Object, Object>) mergeYmlMap.get(entry.getKey()), (Map<Object, Object>) entry.getValue());
}
}
}
// 保存合并结果到目标文件
yam.dump(mergeYmlMap, writer);
writer.close();
writer = null;
}
catch ( final IOException e ) {
throw new ArchiverException(
"Error adding aggregated properties to finalize archive creation. Reason: " + e.getMessage(), e );
}
finally {
IOUtil.close( writer );
}
}
private static void mergeMaps(Map<Object, Object> target, Map<Object, Object> source) {
for (Map.Entry<Object, Object> entry : source.entrySet()) {
if (!target.containsKey(entry.getKey())) {
target.put(entry.getKey(), entry.getValue());
} else if (entry.getValue() instanceof Map){
mergeMaps((Map<Object, Object>) target.get(entry.getKey()), (Map<Object, Object>) entry.getValue());
}
}
}
protected final Logger getLogger() {
if ( logger == null ) {
logger = new ConsoleLogger( Logger.LEVEL_INFO, "" );
}
return logger;
}
@Override
public void enableLogging( final Logger logger ) {
this.logger = logger;
}
@SuppressWarnings( "UnusedDeclaration" )
public String getFilePattern() {
return filePattern;
}
@SuppressWarnings( "UnusedDeclaration" )
public void setFilePattern( final String filePattern ) {
this.filePattern = filePattern;
}
@SuppressWarnings( "UnusedDeclaration" )
public String getOutputPath() {
return outputPath;
}
@SuppressWarnings( "UnusedDeclaration" )
public void setOutputPath( final String outputPath ) {
this.outputPath = outputPath;
}
}
|
合并的containerDescriptorHandler写好啦
在pom文件加上模块依赖
<!-- 将解开的执行包与本工程代码合并打包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<recompressZippedFiles>false</recompressZippedFiles>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifestFile>
${project.build.directory}/work/addpack/mainweb/META-INF/MANIFEST.MF
</manifestFile>
</archive>
<descriptors>
<descriptor>assembly.xml</descriptor> <!-- 加载指定的assembly配置文件 -->
</descriptors>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.southgis.ibase</groupId>
<artifactId>preHandle</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin> |
在assembly.xml文件加上containerDescriptorHandler
<containerDescriptorHandlers> <containerDescriptorHandler> <handlerName>yml-merge</handlerName> <configuration> <filePattern>.*/application.yml</filePattern> <outputPath>BOOT-INF/classes/application.yml</outputPath> </configuration> </containerDescriptorHandler> </containerDescriptorHandlers> |