springboot應用啓動原理(一) 將啓動腳本嵌入jar

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.html

SpringBoot項目爲咱們構建Spring應用帶來了極大的方便,同時SpringBoot在構建Spring應用方面也作出了很大建樹java

衆所周知,SpringBoot能夠經過gradle或者maven插件構建Executable Jar/War Spring Boot Gradle Plugin Reference Guide
除了傳統方式java -jar myapp.jar運行外,還能夠經過myapp.jar start|stop|restart運行,安裝爲systemd服務,經過同名文件myapp.conf配置運行時參數等等高級功能 Installing Spring Boot Applications
講到這裏不少童鞋都會問,這一切都是如何作到的?linux

將啓動腳本嵌入jar

首先,咱們建立一個簡單的示例git

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),打包時選擇main-class爲com.manerfan.springboot.theory.RunnableAppgithub

對於Runnable Jar,總有一個META-INF/MANIFEST.MF文件,記錄Main-Class、Class-Path等信息web

runnable jar

咱們能夠經過java -jar spring-boot-theory.jar來運行,但嘗試直接運行spring-boot-theory.jar時便會報錯spring

clipboard.png

這代表,spring-boot-theory.jar僅僅爲Runnable Jar,而不是Executable Jarshell

使用shell腳本啓動jar

通常狀況,咱們都會藉助shell腳原本運行咱們的jar,以下 runJar.shspringboot

#!/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

clipboard.png

在此基礎上,咱們能夠加入更多控制,以實現runJar.sh start | stop | restart等特性bash

#!/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...

但這樣也只是經過shell腳本控制jar的啓動中止,如何作到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

經過如下語句將shell腳本與jar文件整合到一塊兒 ~劃重點~

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

大功告成!
clipboard.png

一樣,在此基礎上,咱們能夠加入更多控制,以實現exec-spring-boot-theory.jar start | stop | restart等特性

能夠參考 https://coderwall.com/p/ssuax...

Spring Boot的實現原理

springboot項目源碼在https://github.com/spring-pro...,能夠對照查看

咱們從 JarWriter 開始

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

當執行gradle build或mvn package時,會使用JarWriter從新生成jar文件。JarWrite構造函數中,會首先將啓動腳本寫入文件,並設置文件的可執行屬性。
除此以外,JarWriter還有衆多方法,如writeManifest寫入manifest文件、writeNestedLibrary寫入第三方依賴等等,經過JarWriter以構建Executable Jar.
此過程,與上述將shell腳本與jar文件整合效果一致。

可是,launchScript又是什麼?

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

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

默認的LaunchScript爲DefaultLaunchScript,在構造DefaultLaunchScript時,若不指定啓動腳本,則取默認的launch.script,內容見 launch.script
launch.script實現較爲複雜,此處不作解析,launch.script與上述shell腳本的實現思路基本相同,一樣實現了start stop restart等功能,方便安裝爲systemd服務

不一樣的是,launch.script會解析與jar文件同名的conf文件,以實現啓動腳本定製化 Customizing a Script When It Runs

如,咱們實現一個簡單的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插件打包,執行./spring-boot-theory-1.0.0.jar,能夠看到輸出
clipboard.png

訪問 http://localhost:8080 能夠看到 Hello You! 字樣

若要對啓動參數,如監聽端口作修改,除了使用java -jar spring-boot-theory-1.0.0.jar --server.port=8000外,還能夠新建同名文件 spring-boot-theroy-1.0.0.conf,填入內容

RUN_ARGS="--server.port=8000"

再次執行./spring-boot-theory-1.0.0.jar
clipboard.png
監聽端口由默認的8080變爲指定的8000

conf配置文件可配置的內容較多,如使用JAVA_OPTS配置jvm運行參數,使用MODE=service可將程序放入後臺運行等等 Customizing a Script When It Runs

以以下conf配置爲例

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

執行./spring-boot-theory-1.0.0.jar start
clipboard.png

查看該進程運行參數

/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

clipboard.png

總結

SpringBoot實現ExecutableJar的原理,即是將啓動腳本及原有的jar文件(以及第三方依賴包)寫入同一個文件,並給該文件賦可執行權限,結合conf配置文件,使RunnableJar變爲ExecutableJar的同時,得以更加便捷的控制程序的啓動/運行參數


訂閱號

相關文章
相關標籤/搜索