将Springboot的“fat jar” 变成 “thin jar”

来源:互联网 发布:smart白板软件下载 编辑:程序博客网 时间:2024/05/16 11:39

大家都知道spring boot有个很吸引人的特性,那就是可以直接把应用打包成为一个jar/war,而且这个jar/war是可以直接启动的,称之为“fat jar”。

但是有时候我们有这样的需求:

  • 1.将项目中通常版本号确定不会用什么变动的依赖包,不打包进运行包,而是放到外部某个文件夹中,在启动运行包时,再去读取加载
  • 2.经常需要变动的项目模块打包进运行包

这样做的好处:运行包显著变小,传递更加快捷,在经常需要变更的环境下非常有用处!

关于“fat jar”启动不清楚的,可以参考:http://blog.csdn.net/hengyunabc/article/details/50120001,可能和当前版本有些出入,但基本原理是这样。

1.”fat jar” to “thin jar”

1)添加配置:

<plugin>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-maven-plugin</artifactId>    <version>1.4.0.RELEASE</version>  <configuration>    <layout>ZIP</layout>    <executable>true</executable>    <includes>        <include>            <groupId>nothing</groupId>            <artifactId>nothing</artifactId>        </include>        <include>            <groupId>org.cyzy</groupId>            <artifactId>common</artifactId>        </include>    </includes>  </configuration></plugin>
layout

设置为ZIP,此模式下spring-boot-maven-plugin会将Manifest.MF文件中的Main-Class设置为org.springframework.boot.loader.PropertiesLauncher。

PropertiesLauncher源码注释:

Launcher for archives with user-configured classpath and main class via a properties file. This model is often more flexible and more amenable to creating well-behaved OS-level services than a model based on executable jars. loader.path: a comma-separated list of directories (containing file resources and/or nested archives in *.jar or *.zip or archives) or archives to append to the classpath. BOOT-INF/classes,BOOT-INF/lib in the application archive are always used loader.main: the main method to delegate execution to once the class loader is set up. No default, but will fall back to looking for a Start-Class in a MANIFEST.MF, if there is one in ${loader.home}/META-INF.

PropertiesLauncher会从其被启动加载路径的指定路径下:BOOT-INF/classes和BOOT-INF/lib,加载内部classes和lib,同时还提供了通过配置系统变量从外部加载classes和lib的能力。而默认的JarLauncher只能从其被启动加载路径的指定路径下:BOOT-INF/classes和BOOT-INF/lib,加载内部classes和lib。

includes

将需要保留的jar包,按照groupId和artifactId(注意两个都是必填项)include进来。
nothing 代表不存在的依赖包,意思就是什么依赖包都不引入
org.cyzy.common 才是真正的需要保留的依赖包

2)启动

java -Dloader.path=E:/tmp/test/lib -jar gps-server-1.0-SNAPSHOT.jar

PropertiesLauncher会

1.创建LaunchedURLClassLoader(extends URLClassLoader)加载classes和resources,从:

  • gps-server-1.0-SNAPSHOT.jar(JarFileArchive)中的BOOT-INF/classes和BOOT-INF/lib
  • loader.path 指定的外部的目录或Archive

2.从loader.man系统属性或gps-server-1.0-SNAPSHOT.jar中的META-INF/MANIFEST.MF的Start-Class中获取应用的入口类

3.然后启动入口类:

  Thread.currentThread().setContextClassLoader(classLoader);  Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);  Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);  mainMethod.invoke(null, new Object[] { this.args });

2.”fat war” to “thin war”

首先下载spring-boot-tools源码:https://github.com/spring-projects/spring-boot/tree/master/spring-boot-tools
因为项目工程starter都是1.4.0.RELEASE的,所以1.4.0.RELEASE为例。

1).修改spring-boot-loader中的PropertiesLauncher

根据启动类所在路径决定读取的内部的classes和lib的位置。war包读WEB-INF/classes和WEB-INF/lib;其他的读BOOT-INF/classes和BOOT-INF/lib。

private String getBootInf(Archive parent, String property) {    boolean isWar = false;    try {        if(parent.getUrl().getPath().contains(".war!")){            isWar = true;        }    } catch (Exception e) {}    if("cp".equals(property)){        if(isWar){            return WarLauncher.WEB_INF_CLASSES;        }else{            return JarLauncher.BOOT_INF_CLASSES;        }    }    if("lp".equals(property)){        if(isWar){            return WarLauncher.WEB_INF_LIB;        }else{            return JarLauncher.BOOT_INF_LIB;        }    }    return "";}private void addNestedEntries(List<Archive> lib) {    // The parent archive might have "BOOT-INF/lib/" and "BOOT-INF/classes/"    // directories, meaning we are running from an executable JAR. We add nested    // entries from there with low priority (i.e. at end).    String ncp = getBootInf(this.parent, "cp");    String nlp = getBootInf(this.parent, "lp");    try {        log("root archive path:" + parent.getUrl().getPath());        log("nested class path:" + ncp);        log("nested lib path:" + nlp);    } catch (MalformedURLException e) {}    try {        lib.addAll(this.parent.getNestedArchives(new EntryFilter() {            @Override            public boolean matches(Entry entry) {                if (entry.isDirectory()) {                    return entry.getName().startsWith(ncp);                }                return entry.getName().startsWith(nlp);            }        }));    }    catch (IOException ex) {        // Ignore    }}

2).修改spring-boot-loader-tools中的Layouts

如果当前repackage的是war包,则启动类使用PropertiesLauncher

public static class War implements Layout {    ...    @Override    public String getLauncherClassName() {        return "org.springframework.boot.loader.PropertiesLauncher";    }    ...}

1) 2)步源码修改完成后,安装到本地仓库。

3).修改配置:

        <plugin>            <groupId>org.apache.maven.plugins</groupId>            <artifactId>maven-war-plugin</artifactId>            <configuration>                <packagingIncludes>                    app/**,                    bower**/**,                    content/**,                    i18n/**,                    m/**,                    swagger-ui/**,                    templates/**,                    404.html,                    favicon.ico,                    index.html,                    session-expired.html,                    robots.txt,                    WEB-INF/classes/**,                    WEB-INF/lib/spring-boot-1.4.0.RELEASE.jar                </packagingIncludes>            </configuration>        </plugin>        <plugin>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-maven-plugin</artifactId>            <configuration>                <includes>                    <include>                        <groupId>nothing</groupId>                        <artifactId>nothing</artifactId>                    </include>                </includes>            </configuration>        </plugin>
packagingIncludes:

不知道为啥,使用:<packagingExcludes>WEB-INF/lib/**</packagingExcludes>的方式未生效,所以反过来把需要保留的全都包括进来。 同时需要配合spring-boot-maven-plugin的includes一同使用,否则repackage时候又把依赖包打入WEB-INF/lib/。

注意:spring-boot-1.4.0.RELEASE.jar需要打入WEB-INF/lib,否则应用可能会找不到war包中的web resources。

4).启动

java -Dloader.path=E:/tmp/lib2 -jar xxxx.war
0 0