当前位置: 首页 > news >正文

springboot jar是如何启动的

我们先来看一个项目的打完包后的MANIFEST.MF文件:

Manifest‐Version: 1.0
Implementation‐Title: spring‐learn
Implementation‐Version: 0.0.1‐SNAPSHOT
Start‐Class: com.tulingxueyuan.Application
Spring‐Boot‐Classes: BOOT‐INF/classes/
Spring‐Boot‐Lib: BOOT‐INF/lib/
Build‐Jdk‐Spec: 1.8
Spring‐Boot‐Version: 2.1.5.RELEASE
Created‐By: Maven Archiver 3.4.0
Main‐Class: org.springframework.boot.loader.JarLauncher

java -jar执行时会默认寻找Main‐Class对应的类并执行对应的main方法,这里为JarLauncher:

public class JarLauncher extends ExecutableArchiveLauncher {static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";static final String BOOT_INF_LIB = "BOOT-INF/lib/";public JarLauncher() {}protected JarLauncher(Archive archive) {super(archive);}@Overrideprotected boolean isNestedArchive(Archive.Entry entry) {if (entry.isDirectory()) {return entry.getName().equals(BOOT_INF_CLASSES);}return entry.getName().startsWith(BOOT_INF_LIB);}public static void main(String[] args) throws Exception {new JarLauncher().launch(args);}}

当jar的方式启动时,走的是JarLauncher的main方法:

protected void launch(String[] args) throws Exception {JarFile.registerUrlProtocolHandler();ClassLoader classLoader = createClassLoader(getClassPathArchives());launch(args, getMainClass(), classLoader);
}
JarFile#registerUrlProtocolHandler():
private static final String PROTOCOL_HANDLER = "java.protocol.handler.pkgs";
private static final String HANDLERS_PACKAGE = "org.springframework.boot.loader";public static void registerUrlProtocolHandler() {String handlers = System.getProperty(PROTOCOL_HANDLER, "");System.setProperty(PROTOCOL_HANDLER,("".equals(handlers) ? HANDLERS_PACKAGE : handlers + "|" + HANDLERS_PACKAGE));resetCachedUrlHandlers();
}
private static void resetCachedUrlHandlers() {try {URL.setURLStreamHandlerFactory(null);}catch (Error ex) {// Ignore}
}

这里主要注册了java.protocol.handler.pkgs属性,默认为org.springframework.boot.loader

createClassLoader(getClassPathArchives())
protected abstract List<Archive> getClassPathArchives() throws Exception;protected ClassLoader createClassLoader(List<Archive> archives) throws Exception {List<URL> urls = new ArrayList<>(archives.size());for (Archive archive : archives) {urls.add(archive.getUrl());}return createClassLoader(urls.toArray(new URL[0]));
}
protected ClassLoader createClassLoader(URL[] urls) throws Exception {return new LaunchedURLClassLoader(urls, getClass().getClassLoader());
}

archives参数由getClassPathArchives类型传入,默认有2个实现:

protected List<Archive> getClassPathArchives() throws Exception {List<Archive> archives = new ArrayList<>(this.archive.getNestedArchives(this::isNestedArchive));postProcessClassPathArchives(archives);return archives;
}
protected abstract boolean isNestedArchive(Archive.Entry entry);
protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {}//JarLauncher实现
static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
static final String BOOT_INF_LIB = "BOOT-INF/lib/";
protected boolean isNestedArchive(Archive.Entry entry) {if (entry.isDirectory()) {return entry.getName().equals(BOOT_INF_CLASSES);}return entry.getName().startsWith(BOOT_INF_LIB);
}public boolean isNestedArchive(Archive.Entry entry) {if (entry.isDirectory()) {return entry.getName().equals(WEB_INF_CLASSES);}else {return entry.getName().startsWith(WEB_INF_LIB) || entry.getName().startsWith(WEB_INF_LIB_PROVIDED);}}
protected List<Archive> getClassPathArchives() throws Exception {List<Archive> lib = new ArrayList<>();for (String path : this.paths) {for (Archive archive : getClassPathArchives(path)) {if (archive instanceof ExplodedArchive) {List<Archive> nested = new ArrayList<>(archive.getNestedArchives(new ArchiveEntryFilter()));nested.add(0, archive);lib.addAll(nested);}else {lib.add(archive);}}}addNestedEntries(lib);return lib;
}private List<Archive> getClassPathArchives(String path) throws Exception {String root = cleanupPath(handleUrl(path));List<Archive> lib = new ArrayList<>();File file = new File(root);if (!"/".equals(root)) {if (!isAbsolutePath(root)) {file = new File(this.home, root);}if (file.isDirectory()) {debug("Adding classpath entries from " + file);Archive archive = new ExplodedArchive(file, false);lib.add(archive);}}Archive archive = getArchive(file);if (archive != null) {debug("Adding classpath entries from archive " + archive.getUrl() + root);lib.add(archive);}List<Archive> nestedArchives = getNestedArchives(root);if (nestedArchives != null) {debug("Adding classpath entries from nested " + root);lib.addAll(nestedArchives);}return lib;
}

这里主要分析下launch(args, getMainClass(), classLoader)

这里是mainClass的获取:

protected abstract String getMainClass() throws Exception;//默认用的是这个
protected String getMainClass() throws Exception {Manifest manifest = this.archive.getManifest();String mainClass = null;if (manifest != null) {mainClass = manifest.getMainAttributes().getValue("Start-Class");}if (mainClass == null) {throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);}return mainClass;
}protected String getMainClass() throws Exception {String mainClass = getProperty(MAIN, "Start-Class");if (mainClass == null) {throw new IllegalStateException("No '" + MAIN + "' or 'Start-Class' specified");}return mainClass;
}

来看launch方法:

protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {Thread.currentThread().setContextClassLoader(classLoader);createMainMethodRunner(mainClass, args, classLoader).run();
}protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {return new MainMethodRunner(mainClass, args);
}public void run() throws Exception {Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);mainMethod.invoke(null, new Object[] { this.args });
}

通过反射调用mainClass的main方法,项目由此启动起来。这里注意采用的是自定义的classLoader:LaunchedURLClassLoader

http://www.lryc.cn/news/428148.html

相关文章:

  • Android 12系统源码_屏幕设备(二)DisplayAdapter和DisplayDevice的创建
  • 常用Mysql命令
  • IDEA Debug工具
  • ARM64的汇编资源
  • 实验室安全分级分类管理系统在高校中的具体应用
  • 使用 prerenderRoutes 进行预渲染路由
  • 【深度解析】WRF-LES与PALM微尺度气象大涡模拟
  • redis事件机制
  • 【C++】模拟实现vector
  • 【CAN-IDPS】汽车网关信息安全要求以及实验方法
  • EASE-Grid是啥东西?
  • 前端用户管理模块方法及api分析
  • microsoft edge怎么关闭安全搜索
  • Qt | QSQLite内存数据库增删改查
  • 【论文阅读】SegNeXt:重新思考卷积注意力设计
  • 【C++】String类:标准库介绍
  • MS523非接触式读卡器 IC
  • 仓颉编程语言入门 -- Socket 编程与HTTP 编程概述
  • Oracle基本SQL操作-用户角色权限管理
  • Qt-信号和槽(8)
  • 80.游戏的分辨率修改思路与分析
  • MaxKB(二):Ubuntu24.04搭建maxkb开发环境
  • c#实现数据导出为PDF的方式
  • 【联想电脑】:使用拓展坞后转接HDMI,无法识别显示屏
  • Verilog刷题笔记53
  • GoFly快速开发后台框架-后端接口请求返回403提示码就跨域问题/请求端域名拦截问题
  • 设备实时数据采集:开启制造业智能化、自动化的新篇章
  • 【python与java的区别-03(集合、字典)】
  • Java继承
  • Kafka集群搭建的两种方式