SpringBoot應(yīng)用啟動(dòng)原理(一) 將啟動(dòng)腳本嵌入jar

segmentfault 博客原文引用

Spring Boot

Takes an opinionated view of building production-ready Spring applications. Spring Boot favors convention over configuration and is designed to get you up and running as quickly as possible.

SpringBoot項(xiàng)目為我們構(gòu)建Spring應(yīng)用帶來(lái)了極大的方便忆矛,同時(shí)SpringBoot在構(gòu)建Spring應(yīng)用方面也做出了很大建樹(shù)

眾所周知龙巨,SpringBoot可以通過(guò)gradle或者maven插件構(gòu)建Executable Jar/War Spring Boot Gradle Plugin Reference Guide
除了傳統(tǒng)方式java -jar myapp.jar運(yùn)行外土榴,還可以通過(guò)myapp.jar start|stop|restart運(yùn)行糖权,安裝為systemd服務(wù),通過(guò)同名文件myapp.conf配置運(yùn)行時(shí)參數(shù)等等高級(jí)功能 Installing Spring Boot Applications
講到這里很多童鞋都會(huì)問(wèn)吓揪,這一切都是如何做到的?

將啟動(dòng)腳本嵌入jar

首先,我們創(chuàng)建一個(gè)簡(jiǎn)單的示例

package com.manerfan.springboot.theory;

/**
 * @author manerfan
 * @date 2018/3/2
 */

public class RunnableApp {
    public static void main(String[] args) {
        System.out.println("Hello You!");
    }
}

使用eclipse或idea或其他工具膳叨,編譯并打包為jar(spring-boot-theory.jar),打包時(shí)選擇main-class為com.manerfan.SpringBoot.theory.RunnableApp

對(duì)于Runnable Jar痘系,總有一個(gè)META-INF/MANIFEST.MF文件菲嘴,記錄Main-Class、Class-Path等信息

image

我們可以通過(guò)java -jar spring-boot-theory.jar來(lái)運(yùn)行汰翠,但嘗試直接運(yùn)行spring-boot-theory.jar時(shí)便會(huì)報(bào)錯(cuò)

image

這表明龄坪,spring-boot-theory.jar僅僅為Runnable Jar,而不是Executable Jar

使用shell腳本啟動(dòng)jar

一般情況复唤,我們都會(huì)借助shell腳本來(lái)運(yùn)行我們的jar健田,如下 runJar.sh

#!/bin/sh
JAR="/usr/local/spring-boot-theory.jar"
java=java
if test -n "$JAVA_HOME"; then
    java="$JAVA_HOME/bin/java"
fi
exec "$java" -jar $JAR "$@"
exit 1 
image

在此基礎(chǔ)上,我們可以加入更多控制佛纫,以實(shí)現(xiàn)runJar.sh *start | stop | restart*等特性

#!/bin/bash  
# chkconfig: 2345 85 85  
# description: spring boot theory
# processname: spring-boot-theory  
# Created By: manerfan (manerfan.china@gmail.com)  

JAR="/usr/local/spring-boot-theory.jar"
PIDFILE=/data/sms-service/smss.pid  
java=java
if test -n "$JAVA_HOME"; then
    java="$JAVA_HOME/bin/java"
fi

start() {}
stop() {}
restart() {}
status() {}

case "$action" in
start)
  start "$@"; exit $?;;
stop)
  stop "$@"; exit $?;;
restart)
  restart "$@"; exit $?;;
status)
  status "$@"; exit $?;;
*)
  echo "Usage: $0 {start|stop|force-stop|restart|force-reload|status|run}"; exit 1;
esac

exit 0

可以參考 http://blog.csdn.net/zhanngle...

但這樣也只是通過(guò)shell腳本控制jar的啟動(dòng)停止妓局,如何做到Executable Jar呢?

整合shell腳本與Runnable Jar

同樣呈宇,首先是一段shell腳本 runJar.sh

#!/bin/sh
JAR =`which "$0" 2>/dev/null`
[ $? -gt 0 -a -f "$0" ] && JAR="./$0"
java=java
if test -n "$JAVA_HOME"; then
    java="$JAVA_HOME/bin/java"
fi
exec "$java" -jar $JAR "$@"
exit 1 

通過(guò)以下語(yǔ)句將shell腳本與jar文件整合到一起 ~劃重點(diǎn)~

cat runJar.sh spring-boot-theory.jar > exec-spring-boot-theory.jar && chmod +x exec-spring-boot-theory.jar

大功告成好爬!

image

同樣,在此基礎(chǔ)上甥啄,我們可以加入更多控制存炮,以實(shí)現(xiàn)exec-spring-boot-theory.jar *start | stop | restart*等特性

可以參考 https://coderwall.com/p/ssuax...

Spring Boot的實(shí)現(xiàn)原理

SpringBoot項(xiàng)目源碼在https://github.com/spring-pro...,可以對(duì)照查看

我們從 JarWriter 開(kāi)始

public JarWriter(File file, LaunchScript launchScript)
    throws FileNotFoundException, IOException {
  FileOutputStream fileOutputStream = new FileOutputStream(file);
  if (launchScript != null) {
    // 將啟動(dòng)腳本寫(xiě)入文件
    fileOutputStream.write(launchScript.toByteArray());
    // 設(shè)置文件可執(zhí)行屬性
    setExecutableFilePermission(file);
  }
  this.jarOutput = new JarArchiveOutputStream(fileOutputStream);
  this.jarOutput.setEncoding("UTF-8");
}

當(dāng)執(zhí)行g(shù)radle build或mvn package時(shí)蜈漓,會(huì)使用JarWriter重新生成jar文件穆桂。JarWrite構(gòu)造函數(shù)中,會(huì)首先將啟動(dòng)腳本寫(xiě)入文件迎变,并設(shè)置文件的可執(zhí)行屬性充尉。
除此之外,JarWriter還有眾多方法衣形,如writeManifest寫(xiě)入manifest文件驼侠、writeNestedLibrary寫(xiě)入第三方依賴(lài)等等,通過(guò)JarWriter以構(gòu)建Executable Jar.
此過(guò)程谆吴,與上述將shell腳本與jar文件整合效果一致倒源。

但是,launchScript又是什么句狼?

public DefaultLaunchScript(File file, Map<?, ?> properties) throws IOException {
  // 加載啟動(dòng)腳本
  String content = loadContent(file);
  this.content = expandPlaceholders(content, properties);
}

private String loadContent(File file) throws IOException {
  if (file == null) {
    // 默認(rèn)launch.script
    return loadContent(getClass().getResourceAsStream("launch.script"));
  }
  return loadContent(new FileInputStream(file));
}

默認(rèn)的LaunchScript為DefaultLaunchScript笋熬,在構(gòu)造DefaultLaunchScript時(shí),若不指定啟動(dòng)腳本腻菇,則取默認(rèn)的launch.script胳螟,內(nèi)容見(jiàn) launch.script
launch.script實(shí)現(xiàn)較為復(fù)雜昔馋,此處不做解析,launch.script與上述shell腳本的實(shí)現(xiàn)思路基本相同糖耸,同樣實(shí)現(xiàn)了start stop restart等功能秘遏,方便安裝為systemd服務(wù)

不同的是,launch.script會(huì)解析與jar文件同名的conf文件嘉竟,以實(shí)現(xiàn)啟動(dòng)腳本定制化 Customizing a Script When It Runs

如邦危,我們實(shí)現(xiàn)一個(gè)簡(jiǎn)單的web接口

@SpringBootApplication
@RestController
public class WebApp {
    public static void main(String[] args) {
        SpringApplication.run(WebApp.class, args);
    }

    @RequestMapping("/")
    @GetMapping
    public String hello() {
        return "Hello You!";
    }
}

使用spring-boot-gradle-plugin插件打包,執(zhí)行./spring-boot-theory-1.0.0.jar舍扰,可以看到輸出

image

訪問(wèn) http://localhost:8080 可以看到 Hello You! 字樣

若要對(duì)啟動(dòng)參數(shù)倦蚪,如監(jiān)聽(tīng)端口做修改,除了使用java -jar spring-boot-theory-1.0.0.jar --server.port=8000外边苹,還可以新建同名文件 spring-boot-theroy-1.0.0.conf陵且,填入內(nèi)容

RUN_ARGS="--server.port=8000"

再次執(zhí)行./spring-boot-theory-1.0.0.jar

image

監(jiān)聽(tīng)端口由默認(rèn)的8080變?yōu)橹付ǖ?000

conf配置文件可配置的內(nèi)容較多,如使用JAVA_OPTS配置jvm運(yùn)行參數(shù)个束,使用MODE=service可將程序放入后臺(tái)運(yùn)行等等 Customizing a Script When It Runs

以如下conf配置為例

MODE=service
JAVA_OPTS="-Xms1g -Xmx1g -Dfile.encoding=utf-8"
RUN_ARGS="--server.port=8000"

執(zhí)行./spring-boot-theory-1.0.0.jar start

image

查看該進(jìn)程運(yùn)行參數(shù)

/usr/bin/java -Dsun.misc.URLClassPath.disableJarChecking=true -Xms1g -Xmx1g -Dfile.encoding=utf-8 -jar /Users/manerfan/Project/learning/javaspring-boot-theory/build/libs/spring-boot-theory-1.0.0.jar --server.port=8000
image

總結(jié)

SpringBoot實(shí)現(xiàn)ExecutableJar的原理滩报,便是將啟動(dòng)腳本及原有的jar文件(以及第三方依賴(lài)包)寫(xiě)入同一個(gè)文件,并給該文件賦可執(zhí)行權(quán)限播急,結(jié)合conf配置文件脓钾,使RunnableJar變?yōu)镋xecutableJar的同時(shí),得以更加便捷的控制程序的啟動(dòng)/運(yùn)行參數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末桩警,一起剝皮案震驚了整個(gè)濱河市可训,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捶枢,老刑警劉巖握截,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異烂叔,居然都是意外死亡谨胞,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)蒜鸡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)胯努,“玉大人,你說(shuō)我怎么就攤上這事逢防∫杜妫” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵忘朝,是天一觀的道長(zhǎng)灰署。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么溉箕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任晦墙,我火速辦了婚禮,結(jié)果婚禮上肴茄,老公的妹妹穿的比我還像新娘偎痛。我一直安慰自己,他們只是感情好独郎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著枚赡,像睡著了一般氓癌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贫橙,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天贪婉,我揣著相機(jī)與錄音,去河邊找鬼卢肃。 笑死疲迂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的莫湘。 我是一名探鬼主播尤蒿,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼幅垮!你這毒婦竟也來(lái)了腰池?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤忙芒,失蹤者是張志新(化名)和其女友劉穎示弓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體呵萨,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奏属,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了潮峦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片囱皿。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖忱嘹,靈堂內(nèi)的尸體忽然破棺而出铆帽,到底是詐尸還是另有隱情,我是刑警寧澤德谅,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布爹橱,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏愧驱。R本人自食惡果不足惜慰技,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望组砚。 院中可真熱鬧吻商,春花似錦、人聲如沸糟红。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)盆偿。三九已至柒爸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間事扭,已是汗流浹背捎稚。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留求橄,地道東北人今野。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像罐农,于是被迫代替她去往敵國(guó)和親条霜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容