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

大家都知道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)添加配置:

  
  org.springframework.boot  
  spring-boot-maven-plugin  
  1.4.0.RELEASE
  
    ZIP
    true
    
        
            nothing
            nothing
        
        
            org.cyzy
            common
        
    
  

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 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).修改配置:

        
            org.apache.maven.plugins
            maven-war-plugin
            
                
                    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
                
            
        

        
            org.springframework.boot
            spring-boot-maven-plugin
            
                
                    
                        nothing
                        nothing
                    
                
            
        
packagingIncludes:

不知道为啥,使用:WEB-INF/lib/**的方式未生效,所以反过来把需要保留的全都包括进来。 同时需要配合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

你可能感兴趣的:(SpringBoot)