概述
大家都知道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
最后
以上就是坚定啤酒为你收集整理的将Springboot的“fat jar” 变成 “thin jar”的全部内容,希望文章能够帮你解决将Springboot的“fat jar” 变成 “thin jar”所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复