05_JarLauncher源码解读

JarLauncher源码解读

package org.springframework.boot.loader;

import org.springframework.boot.loader.archive.Archive;

/**
 * {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
 * included inside a {@code /BOOT-INF/lib} directory and that application classes are
 * included inside a {@code /BOOT-INF/classes} directory.
 *
 * @author Phillip Webb
 * @author Andy Wilkinson
 */
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的归档的启动程序。这个启动程序假设依赖jar包含在/BOOT-INF/lib目录中,应用程序类包含在/BOOT-INF/classes目录中

JarLauncher的父类ExecutableArchiveLauncher是可执行归档的基类。

ExecutableArchiveLauncher的实现类为:JarLauncherWarLauncher

ExecutableArchiveLauncher的父类Launcher是用于启动程序的基类,该启动程序可以使用由一个或多个存档支持的完全配置的类路径启动应用程序。

JarLauncher main方法

public static void main(String[] args) throws Exception {
    new JarLauncher().launch(args);
}

新建JlaarLauncher后调用类launch方法,launch方法位于Launcher类中

protected void launch(String[] args) throws Exception {
    JarFile.registerUrlProtocolHandler();
    ClassLoader classLoader =           createClassLoader(getClassPathArchives());
    launch(args, getMainClass(), classLoader);
}

启动应用程序。这个方法是一个子类public static void main(String[] args)方法应该调用的初始入口点。

JarFile.registerUrlProtocolHandler();
此方法位于JarFile

 public static void registerUrlProtocolHandler() {
    String handlers = System.getProperty(PROTOCOL_HANDLER, "");
    System.setProperty(PROTOCOL_HANDLER, ("".equals(handlers) ? HANDLERS_PACKAGE
                : handlers + "|" + HANDLERS_PACKAGE));
    resetCachedUrlHandlers();
}   

注册一个java.protocol.handler.pkgs属性,以便URLStreamHandler来处理jar url

ClassLoader classLoader = createClassLoader(getClassPathArchives());

protected abstract List<Archive> getClassPathArchives() throws Exception;返回将用于构造类路径的存档。

子类的实现

protected List<Archive> getClassPathArchives() throws Exception {
    List<Archive> archives = new ArrayList<>(
                this.archive.getNestedArchives(this::isNestedArchive));
    postProcessClassPathArchives(archives);
    return archives;
}

isNestedArchive方法确定指定的JarEntry是否是应该添加到类路径的嵌套项。对每个条目调用该方法一次。

@Override
protected boolean isNestedArchive(Archive.Entry entry) {
    if (entry.isDirectory()) {
        return entry.getName().equals(BOOT_INF_CLASSES);
    }
    return entry.getName().startsWith(BOOT_INF_LIB);
}

添加BOOT-INF/classes/的类和BOOT-INF/lib/的jar到集合archives

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());
}

为指定的url创建类加载器。LaunchedURLClassLoader的父加载是应用加载器。

public LaunchedURLClassLoader(URL[] urls, ClassLoader parent) {
    super(urls, parent);
}
 public URLClassLoader(URL[] urls, ClassLoader parent) {
        super(parent);
        // this is to make the stack depth consistent with 1.1
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        this.acc = AccessController.getContext();
        ucp = new URLClassPath(urls, acc);
 }

为给定的url构造一个新的URLClassLoader。在指定的父类装入器中进行第一次搜索之后,将按照指定的类和资源的顺序搜索url。任何以'/'结尾的URL都被认为是指向一个目录。否则,假定URL引用一个JAR文件,该文件将根据需要下载和打开。
如果存在安全管理器,此方法首先调用安全管理器的checkCreateClassLoader方法,以确保允许创建类加载器。

launch(args, getMainClass(), classLoader);


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 void launch(String[] args, String mainClass, ClassLoader classLoader)
            throws Exception {
    
    Thread.currentThread().setContextClassLoader(classLoader);
    createMainMethodRunner(mainClass, args, classLoader).run();
}

启动给定存档文件和完全配置的类加载器的应用程序.将自定义的类加载器放入线程上下文类下载器中(即替换原应用类加载器)

createMainMethodRunner(mainClass, args, classLoader).run();
创建用于启动应用程序的MainMethodRunner。

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 });
}   

运行到此时才开始执行
SpringApplication.run(MyApplication.class,args);

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容