springboot jar是如何启动的

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

java 复制代码
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:

java 复制代码
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);
	}

	@Override
	protected 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方法:

java 复制代码
protected void launch(String[] args) throws Exception {
    JarFile.registerUrlProtocolHandler();
    ClassLoader classLoader = createClassLoader(getClassPathArchives());
    launch(args, getMainClass(), classLoader);
}
JarFile#registerUrlProtocolHandler():
java 复制代码
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())
java 复制代码
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个实现:

java 复制代码
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);
		}
	}
java 复制代码
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的获取:

java 复制代码
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方法:

java 复制代码
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

相关推荐
qmx_0716 分钟前
HTB-Jerry(tomcat war文件、msfvenom)
java·web安全·网络安全·tomcat
为风而战24 分钟前
IIS+Ngnix+Tomcat 部署网站 用IIS实现反向代理
java·tomcat
技术无疆2 小时前
快速开发与维护:探索 AndroidAnnotations
android·java·android studio·android-studio·androidx·代码注入
罗政5 小时前
[附源码]超简洁个人博客网站搭建+SpringBoot+Vue前后端分离
vue.js·spring boot·后端
架构文摘JGWZ5 小时前
Java 23 的12 个新特性!!
java·开发语言·学习
拾光师6 小时前
spring获取当前request
java·后端·spring
aPurpleBerry6 小时前
neo4j安装启动教程+对应的jdk配置
java·neo4j
我是苏苏6 小时前
Web开发:ABP框架2——入门级别的增删改查Demo
java·开发语言
xujinwei_gingko6 小时前
Spring IOC容器Bean对象管理-Java Config方式
java·spring
2301_789985946 小时前
Java语言程序设计基础篇_编程练习题*18.29(某个目录下的文件数目)
java·开发语言·学习