paulwong

#

SPRING INTEGRATION SCATTERGATHER

场景,餐厅:
  1. 食客下单,有饮品、食物、甜点
  2. 侍应接单,传送给厨房
  3. 厨房分三个子流程处理,即饮品、食物、甜点子流程
  4. 等待三个子流程处理完,合并成一份交付
  5. 如果厨房发现某食物欠缺,会通知侍应,展开错误处理,即通知食客更改食物,再交给厨房
  6. 侍应将交付品传送给食客
有一个主流程、三个子流程和一个聚合流程,聚合流程会聚合三个子流程的产物,通知主流程,再往下走。
并且主流程会感知子流程的错误,并会交给相应错误处理流程处理,且将结果再交给聚合流程。

对应SPRING INTEGRATION 的SCATTERGATHER模式:
@Bean
public IntegrationFlow scatterGatherAndExecutorChannelSubFlow(TaskExecutor taskExecutor) {
    return f -> f
            .scatterGather(
                    scatterer -> scatterer
                            .applySequence(true)
                            .recipientFlow(f1 -> f1.transform(p -> "Sub-flow#1"))
                            .recipientFlow(f2 -> f2
                                    .channel(c -> c.executor(taskExecutor))
                                    .transform(p -> {
                                        throw new RuntimeException("Sub-flow#2");
                                    })),
                    null,
                    s -> s.errorChannel("scatterGatherErrorChannel"));
}

@ServiceActivator(inputChannel = "scatterGatherErrorChannel")
public Message<?> processAsyncScatterError(MessagingException payload) {
    return MessageBuilder.withPayload(payload.getCause().getCause())
            .copyHeaders(payload.getFailedMessage().getHeaders())
            .build();
}

https://github.com/adnanmamajiwala/spring-integration-sample/tree/master/dsl-scatter-gather/src/main/java/com/si/dsl/scattergather

https://docs.spring.io/spring-integration/docs/5.1.x/reference/html/#scatter-gather

posted @ 2021-01-28 10:11 paulwong 阅读(290) | 评论 (0)编辑 收藏

「测试」 - 接口测试 & mock工具Moco

当需要调用第三方HTTP接口时,别人的接口还没完成,可先根据接口定义文档,返回适当的数据,以便开发。

在LINUX上的部署结构为:
├── boot
│   ├── moco-runner-1.1.0-standalone.jar
│   └── .version
├── foo.json
├── logs
│   ├── back
│   └── moco.log
├── moco.sh
└── startup-moco.sh

.version文件:
/path/to/boot/moco-runner-1.1.0-standalone.jar 1.1.0

moco.sh
#!/usr/bin/env bash

# Ensure this file is executable via `chmod a+x moco`, then place it
# somewhere on your $PATH, like ~/bin. The rest of moco will be
# installed upon first run into the ~/.moco directory.

if [ `id -u` -eq 0 ] && [ "$MOCO_ROOT" = "" ]; then
    echo "WARNING: You're currently running as root; probably by accident."
    echo "Press control-C to abort or Enter to continue as root."
    echo "Set MOCO_ROOT to disable this warning."
    read _
fi

echo $*

#export MOCO_HOME="$
{MOCO_HOME:-"$HOME/.moco"}"
export MOCO_HOME=$(cd `dirname $0`; cd boot; pwd)
VERSION_LOG_FILE="$MOCO_HOME/.version"

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

if [ "$HTTP_CLIENT" = "" ]; then
    if type -p curl >/dev/null 2>&1; then
        if [ "$https_proxy" != "" ]; then
            CURL_PROXY="-x $https_proxy"
        fi
        HTTP_CLIENT="curl $CURL_PROXY -f -L -o"
    else
        HTTP_CLIENT="wget -O"
    fi
fi

function download_failed_message 
{
    echo "Failed to download $1"
    echo "It's possible your HTTP client's certificate store does not have the"
    echo "correct certificate authority needed. This is often caused by an"
    echo "out-of-date version of libssl. Either upgrade it or set HTTP_CLIENT"
    echo "to turn off certificate checks
:"
    echo "  export HTTP_CLIENT=\"wget --no-check-certificate -O\" # or"
    echo "  export HTTP_CLIENT=\"curl --insecure -f -L -o\""
    echo "It's also possible that you're behind a firewall haven't yet"
    echo "set HTTP_PROXY and HTTPS_PROXY."
}

function download 
{
    $HTTP_CLIENT "$2.pending" "$1"
    if [ $? == 0 ]; then
        # TODO
: checksum
        mv -f "$2.pending" "$2"
    else
        rm "$2.pending" 2> /dev/null
        download_failed_message "$1"
        exit 1
    fi
}

function parse_tag 
{
   tag_value=`grep "<$2>.*<.$2>" $1 | sed -e "s/^.*<$2/<$2/" | cut -f2 -d">"| cut -f1 -d"<"`
}

function parse_maven_metadata 
{
    MOCO_METADATA_URL="http
://repo1.maven.org/maven2/com/github/dreamhead/moco-runner/maven-metadata.xml"
    MOCO_METADATA="/tmp/maven-metadata.xml"
    download $MOCO_METADATA_URL $MOCO_METADATA
    parse_tag $MOCO_METADATA latest
    LATEST_VERSION=$tag_value
}

function parse_standalone_latest_url 
{
    parse_maven_metadata
    VERSION=${LATEST_VERSION%
}
    LATEST_MOCO_STANDALONE_JAR="moco-runner-$VERSION-standalone.jar"
    MOCO_STANDLONE_URL="http://repo1.maven.org/maven2/com/github/dreamhead/moco-runner/$LATEST_VERSION/$LATEST_MOCO_STANDALONE_JAR"
}

function install 
{
    echo "Install moco"
    echo "Parse the latest version of moco"
    parse_standalone_latest_url
    echo "Download the latest moco
: $LATEST_VERSION"
    MOCO_STANDALONE="$MOCO_HOME/$LATEST_MOCO_STANDALONE_JAR"
    echo "$MOCO_STANDALONE $LATEST_VERSION" >> $VERSION_LOG_FILE
    download $MOCO_STANDLONE_URL $MOCO_STANDALONE
}

function load_current_version 
{
    read MOCO_STANDALONE CURRENT_VERSION < $VERSION_LOG_FILE
    if [[ "$(uname)" -ne "Darwin" && "$(expr substr $(uname -s) 2 6)" == "CYGWIN"   ]];then
        MOCO_STANDALONE=`cygpath -m "$MOCO_STANDALONE"`
    fi
}

function usage 
{
  printf "
options
:
       help      show help
       start     start server, e.g. moco start -p 12306 -c configfile.json
       shutdown  shutdown moco server 
       upgrade   upgrade moco
"
}

if [ ! -e "$MOCO_HOME" ]
then
    mkdir "$MOCO_HOME"
    install
fi

if [ "$1" = "start" ]; then
    echo "Starting"
    load_current_version
    exec "$JAVACMD" -jar "$MOCO_STANDALONE" $*
elif [ "$1" = "http" ]; then
    echo "Starting HTTP server"
    load_current_version
    exec "$JAVACMD" -jar "$MOCO_STANDALONE" $*
elif [ "$1" = "https" ]; then
    echo "Starting HTTPS server"
    load_current_version
    exec "$JAVACMD" -jar "$MOCO_STANDALONE" $*
elif [ "$1" = "socket" ]; then
    echo "Starting Socket server"
    load_current_version
    exec "$JAVACMD" -jar "$MOCO_STANDALONE" $*
elif [ "$1" = "shutdown" ]; then
    echo "Shutting down server"
    load_current_version
    exec "$JAVACMD" -jar "$MOCO_STANDALONE" $*
elif [ "$1" = "upgrade" ]; then
    echo "Check the new version"
    parse_maven_metadata
    load_current_version

    if [ "$LATEST_VERSION" = "$CURRENT_VERSION" ]; then
        echo "The current version of moco is the latest"
    else
        echo "Upgrading"
        rm $VERSION_LOG_FILE
        install
    fi
elif [ "$1" = "version" ]; then
    load_current_version
    echo "Moco version: $CURRENT_VERSION"
elif [ "$1" = "help" ]; then
    usage
else
    usage
fi

这是根据GIT上的原始文件作的修改。

startup-moco.sh
CMD_PATH=$(cd `dirname $0`; pwd)

# 项目日志输出绝对路径
LOG_DIR=$
{CMD_PATH}"/logs"
LOG_FILE="moco.log"
LOG_PATH="$
{LOG_DIR}/${LOG_FILE}"

# 当前时间
NOW=`date +'%Y-%m-%m-%H-%M-%S'`
NOW_PRETTY=`date +'%Y-%m-%m %H:%M:%S'`

# 启动日志
STARTUP_LOG="================================================ $
{NOW_PRETTY} ================================================\n"

# 日志备份目录
LOG_BACK_DIR="$
{LOG_DIR}/back/"

# 如果logs文件夹不存在,则创建文件夹
if [[ ! -d "$
{LOG_DIR}" ]]; then
  mkdir "$
{LOG_DIR}"
fi

# 如果logs/back文件夹不存在,则创建文件夹
if [[ ! -d "$
{LOG_BACK_DIR}" ]]; then
  mkdir "$
{LOG_BACK_DIR}"
fi

# 如果项目运行日志存在,则重命名备份
if [[ -f "$
{LOG_PATH}" ]]; then
    mv $
{LOG_PATH} "${LOG_BACK_DIR}/${APPLICATION}_back_${NOW}.log"
fi

# 创建新的项目运行日志
echo "" > $
{LOG_PATH}

# 可支持多个json配置文件
$CMD_PATH/moco.sh http -p 8088 -g "${CMD_PATH}/root.json" > ${LOG_PATH} 2>&1 &

# 打印启动日志
echo -e $
{STARTUP_LOG}

root.json
[
    
{
        "context"
: "/service-a",
        "include": "foo.json"
    
},
    
{
        "context"
: "/service-b",
        "include": "bar.json"
    
}
]

foo.json
[
    
{
        "request"
: {
            "method": "post",
            "forms": {
                "method": "uploadKycInfo"
            
}
        },
        "response": 
{
            "json"
: {
                "response": {
                    "subcode": "10000",
                    "submsg": "Success",
                    "sndDt": "20210121101800",
                    "remark": "上传验证基本信息",
                    "msgBody": {
                        "merOrdrNo": "A00120210121654321",
                        "retCode": "00000",
                        "retMsg": "成功/处理完成",
                        "remark": "上传详情或上传信息的简要描述"
                    
}
                },
                "code": "0000",
                "msg": "处理完成",
                "sign": "DF2659FE3EB8184561135D9F55F5EF5"
            }
        }
    }
]

访问路径:
http://ip:port/service-a/

https://github.com/dreamhead/moco/blob/master/moco-doc/apis.md
https://zhuanlan.zhihu.com/p/60076337 
https://www.programmersought.com/article/68272293688/

posted @ 2021-01-21 14:09 paulwong 阅读(568) | 评论 (0)编辑 收藏

Java8中文件转Base64和Base64转文件

有几个项目中,都需要将图片或者数字证书的文件转为Base64,昨天写代码的时候,发现在jdk8中本就含有关于Base64的API。

从此后不再需要其他的jar包来转换Base64了!!!

据说是JDK8加入的。


先是将文件转为Base64:
public String encryptToBase64(String filePath) {
        if (filePath == null) {
            return null;
        }
        try {
            byte[] b = Files.readAllBytes(Paths.get(filePath));
            return Base64.getEncoder().encodeToString(b);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        return null;
    }

Files、Paths类是JDK7里加入的,读取文件不再需要调用IO包里的FileInputStream,简单便捷。
字符串参数filePath是文件的路径。

首先是将文件读成二进制码,然后通过Base64.getEncoder().encodeToString()方法将二进制码转换为Base64值。


然后是将Base64转为文件:
public String decryptByBase64(String base64, String filePath) {
        if (base64 == null && filePath == null) {
            return "生成文件失败,请给出相应的数据。";
        }
        try {
            Files.write(Paths.get(filePath), Base64.getDecoder().decode(base64),StandardOpenOption.CREATE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "指定路径下生成文件成功!";
    }

字符串参数base64指的是文件的Base64值,filePath是指的文件将要保存的位置。
通过Files.write()方法轻松将文件写入指定位置,不再调用FileOutStream方法。

第三个参数StandardOpenOption.CREATE是处理文件的方式,我设置的是不管路径下有或没有,都创建这个文件,有则覆盖。

在StandardOpenOption类中有很多参数可调用,不再累赘。

posted @ 2021-01-07 09:44 paulwong 阅读(3743) | 评论 (0)编辑 收藏

微服务架构设计模式

微服务架构及设计模式
http://dockone.io/article/9680

微服务架构10个最重要的设计模式
https://developer.51cto.com/art/202012/635852.htm

GitHub标星127K!字节内部必刷“微服务架构设计模式”
https://blog.csdn.net/a159357445566/article/details/110006542

微服务架构及其最重要的10个设计模式
https://www.infoq.cn/article/Kdw69bdimlX6FSGz1bg3

Effective Microservices: 10 Best Practices
https://towardsdatascience.com/effective-microservices-10-best-practices-c6e4ba0c6ee2

Microservice Architecture and its 10 Most Important Design Patterns
https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41




posted @ 2021-01-06 14:44 paulwong 阅读(351) | 评论 (0)编辑 收藏

21个2021年软件开发趋势预测

     摘要: 2020年是最近历史上前所未有的一年。在过去的一百年中,人类没有经历过像COVID-19这样的全球性大流行。它影响了我们星球上的所有国家,部门和几乎所有个人。 好消息是,我们已经准备好疫苗,终于可以充满乐观和希望,迎接新的一年2021年。 2020年对于软件开发行业来说是重要的一年,在许多领域都取得了明显的突破。COVID-19大大加快了数字化转型,到2021年这种趋势将更加明显。 在软...  阅读全文

posted @ 2021-01-05 09:56 paulwong 阅读(346) | 评论 (0)编辑 收藏

http connection存活期

HTTP1.1的链接,默认是长链接,不会主动关闭。
LINUX会默认保留链接5天再关闭。
建立HTTP链接其实也是调用TCL的协议去建立,包括开始的时候有三次握手,关闭的时候有四次握手。关闭链接双方都可以发起。
但这些链接可能会被防火墙关掉而不通知建立链接的双方,因此设置需设置链接的存活期。
使用httpClient的链接池时,要设置池中的链接存活期或设置存活策略。
检测存活期只在每次发送数据时,才检测取出的链接是否超过存活期,如超过则关闭。
设置存活期的策略:

import java.util.concurrent.TimeUnit;

import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpResponse;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args;

public class MyConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy{
    
    
    private int timeToLive;
    private TimeUnit timeUnit;

    public MyConnectionKeepAliveStrategy(int timeToLive, TimeUnit timeUnit) {
        this.timeToLive = timeToLive;
        this.timeUnit = timeUnit;
    }

    @Override
    public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {
        Args.notNull(response, "HTTP response");
        final HeaderElementIterator it = new BasicHeaderElementIterator(
                response.headerIterator(HTTP.CONN_KEEP_ALIVE));
        while (it.hasNext()) {
            final HeaderElement he = it.nextElement();
            final String param = he.getName();
            final String value = he.getValue();
            if (value != null && param.equalsIgnoreCase("timeout")) {
                try {
                    return Long.parseLong(value) * 1000;
                } catch(final NumberFormatException ignore) {
                    //do nothing
                }
            }
        }
        return timeUnit.toMillis(timeToLive);
    }

}

《HttpClient官方文档》2.6 连接维持存活策略
http://ifeve.com/httpclient-2-6/

httpclient连接池管理,你用对了?
http://ifeve.com/http-connection-pool/

HttpClient连接池的一些思考
https://zhuanlan.zhihu.com/p/85524697

HTTP协议的Keep-Alive 模式
https://www.jianshu.com/p/49551bda6619

posted @ 2020-12-31 15:08 paulwong 阅读(345) | 评论 (0)编辑 收藏

开始日期与结束日期的用法

通常要用到取某个时间段内的数据,那么时间段要如何定义?
取2020-12-01这天的数据,"2020-12-01 00:00:00" <= time < "2020-12-02 00:00:00"。
apache common3中提供了相应的方法:

startDate = DateUtils.parseDate(startDateStr, DATE_PATTERN);
            
String endDateStr = args.getOptionValues(END_DATE).get(0);
endDate = DateUtils.parseDate(endDateStr, DATE_PATTERN);

//清零开始日期,返回类似2020-12-01 00:00:00            
startDate = DateUtils.truncate(startDate, Calendar.DATE);
//取结束日期的上限,返回隔天的时间,2020-12-02 00:00:00
endDate = DateUtils.ceiling(endDate, Calendar.DATE);"。
apache common3中提供了相应的方法:

posted @ 2020-12-29 10:37 paulwong 阅读(407) | 评论 (0)编辑 收藏

TCP 連線狀態機制與流程

https://dev.twsiyuan.com/2017/09/tcp-states.html
在開發基於 HTTP 的網路應用服務時,當有大量連線要求,或是與長連線 (Persistent connection) 要求時,常常遇到底層 TCP 的連線斷線錯誤,導致服務不穩定。因此研究了解 TCP 的連線狀態機制,並嘗試用自己的方式整理筆記,希望能從基礎知識中找到解決錯誤的線索,或是任何能更進一步優化服務的手段。

僅紀錄 TCP 連線狀態以及建立或是斷開連線流程,關於進一步的 TCP 封包協定可參考 Reference 連線。

TCP 建立連線 (Open)

通常的 TCP 連線建立流程與狀態,需要三次的訊息交換來建立連線 (three-way handshaking):

TCP 建立連線流程圖

TCP 建立連線流程圖

其中左邊通常為 server,右邊則為 client,文字流程描述:

  1. Server 建立 TCB,開啟監聽連線,進入狀態 LISTENING
  2. Client 發出連線要求 SYN,進入狀態 SYN-SENT,等待回應
  3. Server 收到 SYN 要求,回應連線傳送 SYN+ACK,並進入狀態 SYN-RCVD (SYN-RECEIVED)
  4. Client 收到 SYN+ACK 確認完成連線進入狀態 ESTABLISHED,並送出 ACK
  5. Server 收到 ACK 確認連線完成,也進入狀態 ESTABLISHED
  6. 雙方開始傳送交換資料

該些名詞與狀態說明:

  • CLOSED:連線關閉狀態
  • LISTENING:監聽狀態,被動等待連線
  • SYN-SENT:主動送出連線要求 SYN,並等待對方回應
  • SYN-RCVD:收到連線要求 SYN,送出己方的 SYN+ACK 後,等待對方回應
  • ESTABLISHED:確定完成連線,可開始傳輸資料
  • TCB:Transmission Control Block,see wiki
  • SYN:Synchronous,表示與對方建立連線的同步符號
  • ACK:Acknowledgement,表示發送的數據已收到無誤

在建立連線時,可能會發生雙方同步建立連線的情形 (Simultaneous open),常見於 P2P 的應用中,其 TCP 建立連線的流程不太一樣:

TCP 同步建立連線流程圖

TCP 同步建立連線流程圖

畫成 TCP 狀態流程圖會是這樣:

TCP Open 狀態圖

TCP Open 狀態圖

TCP 斷開連線 (Close)

TCP 關閉流程如下,比建立連線還要複雜一些,需要經過四次的訊息交換 (four-way handshaking),要注意的是可以是由 server 發起主動關閉,抑或是 client 發起主動關閉:

TCP 關閉連線流程圖

TCP 關閉連線流程圖

其中左邊通常為 client 狀態 (由 client 主動發起關閉連線),右邊則為 server 狀態,文字流程描述:

  1. Client 準備關閉連線,發出 FIN,進入狀態 FIN-WAIT-1
  2. Server 收到 FIN,發回收到的 ACK,進入狀態 CLOSE-WAIT,並通知 App 準備斷線
  3. Client 收到 ACK,進入狀態 FIN-WAIT-2,等待 server 發出 FIN
  4. Server 確認 App 處理完斷線請求,發出 FIN,並進入狀態 LAST-ACK
  5. Client 收到 FIN,並回傳確認的 ACK,進入狀態 TIME-WAIT,等待時間過後正式關閉連線
  6. Server 收到 ACK,便直接關閉連線

該些名詞與狀態說明:

  • ESTABLISHED:連線開啟狀態
  • CLOSE-WAIT:等待連線關閉狀態,等待 App 回應
  • LAST-ACK:等待連線關閉狀態,等待遠端回應 ACK 後,便關閉連線
  • FIN-WAIT-1:等待連線關閉狀態,等待遠端回應 ACK
  • FIN-WAIT-2:等待連線關閉狀態,等待遠端回應 FIN
  • TIME-WAIT:等待連線關閉狀態,等段一段時候,保證遠端有收到其 ACK 關閉連線 (網路延遲問題)
  • CLOSED:連線關閉狀態
  • FIN:表示關閉連線的同步符號
  • ACK:Acknowledgement,表示發送的數據已收到無誤

有可能連線的雙方同時發起關閉,雖然機率還蠻低的:

TCP 同步關閉連線流程圖

TCP 同步關閉連線流程圖

這邊多一個狀態:

  • CLOSING:等待連線關閉狀態,等待遠端回應 ACK

畫成 TCP 狀態流程圖會是這樣:

TCP Close 狀態圖

TCP Close 狀態圖

查詢現在電腦的 TCP 狀態

查詢目前所有的連線狀態 (Windows & Linux):

netstat -a

Reference

posted @ 2020-12-29 10:04 paulwong 阅读(235) | 评论 (0)编辑 收藏

httpClient Connection Pool使用的正确姿势

import java.util.concurrent.TimeUnit;

import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class HttpClientConfiguration {
    
    
    @Bean
      public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
        PoolingHttpClientConnectionManager result = 
                new PoolingHttpClientConnectionManager(5, TimeUnit.MINUTES);
        result.setMaxTotal(20);
        result.setDefaultMaxPerRoute(20);
        return result;
      }

      @Bean
      public RequestConfig requestConfig(KycProperties kycProperties) {
          return RequestConfig
                        .custom()
                      .setConnectionRequestTimeout(kycProperties.getHttpConnectionTimeout())
                      .setConnectTimeout(kycProperties.getHttpConnectionTimeout())
                      .setSocketTimeout(kycProperties.getHttpConnectionTimeout())
                      .build();
      }

      @Bean
      public CloseableHttpClient httpClient(PoolingHttpClientConnectionManager poolingHttpClientConnectionManager, RequestConfig requestConfig) {
          return HttpClients
                      .custom()
                      .setConnectionManager(poolingHttpClientConnectionManager)
                      .setDefaultRequestConfig(requestConfig)
                      .build();
      }

}

Troubleshooting Spring's RestTemplate Requests Timeout
https://tech.asimio.net/2016/12/27/Troubleshooting-Spring-RestTemplate-Requests-Timeout.html a

httpclient超时重试记录
https://blog.csdn.net/wanghao112956/article/details/102967930

posted @ 2020-12-29 09:36 paulwong 阅读(1169) | 评论 (0)编辑 收藏

Maven project version inheritance

A/pom.xml:
<groupId>com.dummy.bla</groupId>
<artifactId>parent</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>

B/pom.xml
<parent>
    <groupId>com.dummy.bla</groupId>
    <artifactId>parent</artifactId>
    <version>0.1-SNAPSHOT</version>     
</parent>

<groupId>com.dummy.bla.sub</groupId>
<artifactId>kid</artifactId>

B/pom.xml中需要显式注明父pom的版本号,但从MAVEN3.5之后,可以用变量表示了:

A/pom.xml:
<groupId>com.dummy.bla</groupId>
<artifactId>parent</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>

<properties>
    <revision>0.1-SNAPSHOT</revision>
</properties>

B/pom.xml
<parent>
    <groupId>com.dummy.bla</groupId>
    <artifactId>parent</artifactId>
    <version>${revision}</version>     
</parent>

<groupId>com.dummy.bla.sub</groupId>
<artifactId>kid</artifactId>

https://stackoverflow.com/questions/10582054/maven-project-version-inheritance-do-i-have-to-specify-the-parent-version/51969067#51969067


https://maven.apache.org/maven-ci-friendly.html

posted @ 2020-12-16 09:48 paulwong 阅读(286) | 评论 (0)编辑 收藏

仅列出标题
共110页: First 上一页 6 7 8 9 10 11 12 13 14 下一页 Last