大家都知道spring boot有个很吸引人的特性,那就是可以直接把应用打包成为一个jar/war,而且这个jar/war是可以直接启动的,称之为“fat jar”。
但是有时候我们有这样的需求:
这样做的好处:运行包显著变小,传递更加快捷,在经常需要变更的环境下非常有用处!
关于“fat jar”启动不清楚的,可以参考:http://blog.csdn.net/hengyunabc/article/details/50120001,可能和当前版本有些出入,但基本原理是这样。
org.springframework.boot
spring-boot-maven-plugin
1.4.0.RELEASE
ZIP
true
nothing
nothing
org.cyzy
common
设置为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。
将需要保留的jar包,按照groupId和artifactId(注意两个都是必填项)include进来。
nothing 代表不存在的依赖包,意思就是什么依赖包都不引入
org.cyzy.common 才是真正的需要保留的依赖包
java -Dloader.path=E:/tmp/test/lib -jar gps-server-1.0-SNAPSHOT.jar
PropertiesLauncher会
1.创建LaunchedURLClassLoader(extends URLClassLoader)加载classes和resources,从:
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 });
首先下载spring-boot-tools源码:https://github.com/spring-projects/spring-boot/tree/master/spring-boot-tools
因为项目工程starter都是1.4.0.RELEASE的,所以1.4.0.RELEASE为例。
根据启动类所在路径决定读取的内部的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
}
}
如果当前repackage的是war包,则启动类使用PropertiesLauncher
public static class War implements Layout {
...
@Override
public String getLauncherClassName() {
return "org.springframework.boot.loader.PropertiesLauncher";
}
...
}
1) 2)步源码修改完成后,安装到本地仓库。
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
不知道为啥,使用:
的方式未生效,所以反过来把需要保留的全都包括进来。 同时需要配合spring-boot-maven-plugin的includes一同使用,否则repackage时候又把依赖包打入WEB-INF/lib/。
注意:spring-boot-1.4.0.RELEASE.jar需要打入WEB-INF/lib,否则应用可能会找不到war包中的web resources。
java -Dloader.path=E:/tmp/lib2 -jar xxxx.war