jar Exploration of Springboot Source Analysis

Keywords: Java SpringBoot snapshot Spring

Abstract:

  • Using IDEA and other tools to pack will appear springboot-0.0.1-SNAPSHOT.jar,springboot-0.0.1-SNAPSHOT.jar.original, have said the relationship between them before, next we will find out, what is the relationship between them?

Document comparison:

  • Enter the target directory and unzip springboot-0.0.1-SNAPSHOT.jar-d jar command decompresses springboot-0.0.1-SNAPSHOT.jar to the jar directory

  • Enter the target directory and unzip spring boot-0.0.1-SNAPSHOT.jar.original-d original command extracts spring boot-0.0.1-SNAPSHOT.jar.original to the original directory

The previous article analyzed that springboot-0.0.1-SNAPSHOT.jar.original can not be executed. After repackage, springboot-0.0.1-SNAPSHOT.jar becomes our executable fat jar. Comparing the above files, we will find that the executable fat jar is different from the original jar directory. The most important thing is the package of org.springframework.boot.loader, which is our usual java-jar. The secret of starting ringboot-0.0.1-snapshot.jar command. The contents of the MANIFEST.MF file contain a lot of key information.

Manifest-Version: 1.0
Start-Class: com.github.dqqzj.springboot.SpringbootApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.1.6.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher

I believe you don't have to say that everyone can understand that Main-Class: org.springframework.boot.loader.JarLauncher is the entry of our java -jar command, which will be analyzed later. Start-Class: com.github.dqqzj.springboot.SpringbootApplication is the main function of our program.

Springboot jar boot source code analysis

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);
    }
   /**
    * Judging whether to archive a file or a directory of a file system, you can guess that a file system-based startup is as good as a file system-based startup.
    */
    protected boolean isNestedArchive(Entry entry) {
        return entry.isDirectory() ? entry.getName().equals("BOOT-INF/classes/") : entry.getName().startsWith("BOOT-INF/lib/");
    }

    public static void main(String[] args) throws Exception {
    /**
     * Enter the parent initialization constructor Executable Archive Launcher
     * launch Method to Launcher
     */
        (new JarLauncher()).launch(args);
    }
}

public abstract class ExecutableArchiveLauncher extends Launcher {
    private final Archive archive;

    public ExecutableArchiveLauncher() {
        try {
    /**
     * Load resources using parent Launcher, including all archives under BOOT-INF classes and lib
     */
            this.archive = this.createArchive();
        } catch (Exception var2) {
            throw new IllegalStateException(var2);
        }
    }

    protected ExecutableArchiveLauncher(Archive archive) {
        this.archive = archive;
    }

    protected final Archive getArchive() {
        return this.archive;
    }
    /**
     * Getting our application main function from the archive file
     */
    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);
        } else {
            return mainClass;
        }
    }

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

    protected abstract boolean isNestedArchive(Entry entry);

    protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {
    }
}

public abstract class Launcher {
    public Launcher() {
    }

    protected void launch(String[] args) throws Exception {
      /**
       *Register Protocol Processor, because Springboot is jar in jar, rewrite the jar protocol to read the archive file
       */
        JarFile.registerUrlProtocolHandler();
        ClassLoader classLoader = this.createClassLoader(this.getClassPathArchives());
      /**
       * this.getMainClass()Deliver the subclass Executable ArchiveLauncher implementation
       */
        this.launch(args, this.getMainClass(), classLoader);
    }

    protected ClassLoader createClassLoader(List<Archive> archives) throws Exception {
        List<URL> urls = new ArrayList(archives.size());
        Iterator var3 = archives.iterator();

        while(var3.hasNext()) {
            Archive archive = (Archive)var3.next();
            urls.add(archive.getUrl());
        }

        return this.createClassLoader((URL[])urls.toArray(new URL[0]));
    }
    /**
     * This class loader is a key part of fat jar, because the traditional class loader can not read the jar in jar model, so spring boot has carried out its own implementation.
     */
    protected ClassLoader createClassLoader(URL[] urls) throws Exception {
        return new LaunchedURLClassLoader(urls, this.getClass().getClassLoader());
    }
   
    protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
        Thread.currentThread().setContextClassLoader(classLoader);
        this.createMainMethodRunner(mainClass, args, classLoader).run();
    }
    /**
     * Create the application main function runner
     */
    protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
        return new MainMethodRunner(mainClass, args);
    }

    protected abstract String getMainClass() throws Exception;

    protected abstract List<Archive> getClassPathArchives() throws Exception;
   /**
          * Get our start jar Archive
            */
    protected final Archive createArchive() throws Exception {
        ProtectionDomain protectionDomain = this.getClass().getProtectionDomain();
        CodeSource codeSource = protectionDomain.getCodeSource();
        URI location = codeSource != null ? codeSource.getLocation().toURI() : null;
        String path = location != null ? location.getSchemeSpecificPart() : null;
        if (path == null) {
            throw new IllegalStateException("Unable to determine code source archive");
        } else {
            File root = new File(path);
            if (!root.exists()) {
                throw new IllegalStateException("Unable to determine code source archive from " + root);
            } else {
                return (Archive)(root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));
            }
        }
    }
}

public class MainMethodRunner {
    private final String mainClassName;
    private final String[] args;
   
    public MainMethodRunner(String mainClass, String[] args) {
        this.mainClassName = mainClass;
        this.args = args != null ? (String[])args.clone() : null;
    }
    /**
     * The final method of execution can be found to be the main function of our application invoked using reflection.
     */
    public void run() throws Exception {
        Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
        Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
        mainMethod.invoke((Object)null, this.args);
    }
}

Summary:

There are too many contents, no archives, protocol processors, packaged war s can also be started by command, etc. Interested readers please debug and add dependencies in person.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-loader</artifactId>
        </dependency>

IDEA configures startup classes

Posted by jeevan_y21 on Fri, 04 Oct 2019 03:21:15 -0700