SpringBoot的jar包如何启动的实现

目录
  • 一、简介
  • 二、jar包的内部结构
  • 三、加载过程
    • 1.使用到的一些类
    • 2.过程分析
  • 四、总结

一、简介

​ 使用过SprongBoot打过jar包的都应该知道,目标文件一般都会生成两个文件,一个是以.jar的包,一个是.jar.original文件。那么使用SpringBoot会打出两个包,而.jar.original的作用是什么呢?还有就是java -jar是如何将一个SpringBoot项目启动,之间都进行了那些的操作?

​ 其实.jar.originalmavenSpringBoot重新打包之前的原始jar包,内部只包含了项目的用户类,不包含其他的依赖jar包,生成之后,SpringBoot重新打包之后,最后生成.jar包,内部包含了原始jar包以及其他的引用依赖。以下提及的jar包都是SpringBoot二次加工打的包。

二、jar包的内部结构

SpringBoot打出的jar包,可以直接通过解压的方式查看内部的构造。一般情况下有三个目录。

  • BOOT-INF:这个文件夹下有两个文件夹classes用来存放用户类,也就是原始jar.original里的类;还有一个是lib,就是这个原始jar.original引用的依赖。
  • META-INF:这里是通过java -jar启动的入口信息,记录了入口类的位置等信息。
  • org:Springboot loader的代码,通过它来启动。

这里主要介绍一下/BOOT-INF/MANIFEST.MF文件

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: cn.com.springboot.center.AuthEenterBootstrap

Main-Class:记录了java -jar的启动入口,当使用该命令启动时就会调用这个入口类的main方法,显然可以看出,Springboot转移了启动的入口,不是用户编写的xxx.xxx.BootStrap的那个入口类。

Start-Class:记录了用户编写的xxx.xxx.BootStrap的那个入口类,当内嵌的jar包加载完成之后,会使用LaunchedURLClassLoader线程加载类来加载这个用户编写的入口类。

三、加载过程

1.使用到的一些类

3.1.1 Archive

​ 归档文件接口,实现迭代器接口,它有两个子类,一个是JarFileArchivejar包文件使用,提供了返回这个jar文件对应的url、或者这个jar文件的MANIFEST文件数据信息等操作。是ExplodedArchive是文件目录的使用也有获取这个目录url的方法,以及获取这个目录下的所有Archive文件方法。

3.1.2 Launcher

​ 启动程序的基类,这边最后是通过JarLauncher#main()来启动。ExecutableArchiveLauncher是抽象类,提供了获取Start-Class类路径的方法,以及是否还有内嵌对应文件的判断方法和获取到内嵌对应文件集合的后置处理方法的抽象,由子类JarLauncherWarLauncher自行实现。

3.1.3 Spring.loader下的JarFile和JarEntry

jarFile继承于jar.util.jar.JarFileJarEntry继承于java.util.jar.JarEntry,对原始的一些方法进行重写覆盖。每一个JarFileArchive都拥有一个JarFile方法,用于存储这个jar包对应的文件,而每一个JarFile都有一个JarFileEntries,JarFileEntries是一个迭代器。总的来说,在解析jar包时,会将jar包内的文件封装成JarEntry对象后由JarFile对象保存文件列表的迭代器。所以JarFileArchiveJarFileEntries之间是通过JarFile连接,二者都可以获取到JarFile对象。

2.过程分析

MANIFEST.MF文件中的Main-class指向入口开始。

创建JarLauncher并且通过它的launch()方法开始加载jar包内部信息。

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

JarLauncher的空构造方法时一个空实现,刚开始看的时候还懵了一下,以为是在后续的操作中去加载的文件,其实不然,在创建时由父类ExecutableArchiveLauncher的构造方法去加载的文件。

加载为归档文件对象:

this.archive = createArchive();

具体的加载方法:判断路径是否是一个文件夹,是则返回ExplodedArchive对象,否则返回JarFileArchive 进入JarFileArchive类:通过这个new方法创建JarFile对象

public class JarFileArchive implements Archive {

  public JarFileArchive(File file, URL url) throws IOException {
    this(new JarFile(file));
    this.url = url;
  }
}

进入到JarFile方法:通过RandomAccessDataFile读取文件的内容,并传递给本类中的方法进行具体的解析。

public class JarFile extends java.util.jar.JarFile {

  public JarFile(File file) throws IOException {
    this(new RandomAccessDataFile(file));
  }
}

进入jarLauncherlaunch方法:注册URL协议的处理器,没有指定时,默认指向org.springframework.boot.loader包路径,获取类路径下的归档文件Archive并通过这些归档文件的URL,创建线程上下文类加载器,使用类加载器和用户编写的启动入口类,通过反射调用它的main方法。

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

JarLaunchergetClassPathArchives是在ExecutableArchiveLauncher中实现:获取归档文件中满足EntryFilterg过滤器的项,isNestedArchive方法由具体的之类实现。获取到当前归档文件下的所有子归档文件之后的后置操作,是一个扩展点。在JarLauncher中是一个空实现。

JarLauncher的具体实现,这里通过判断是否在BOOT-INF/lib/包下返回true 也就是说只会把jar包下的BOOT-INF/lib/下的文件加载为Archive对象

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

JarFileArchivegetNestedArchives方法:若匹配器匹配到则获取内嵌归档文件。

具体的获取内嵌归档文件逻辑:根据具体的Entry对象,创建JarFile对象并封装成归档文件对象后返回。

protected Archive getNestedArchive(Entry entry) throws IOException {
    try {
        JarFile jarFile = this.jarFile.getNestedJarFile(jarEntry);
        return new JarFileArchive(jarFile);
    }
}

获取到参数entry对应的RandomAccessData对象,这里根据springboot扩展的url协议,在父路径的基础上添加!/来标记子包。

private JarFile createJarFileFromFileEntry(JarEntry entry) throws IOException {
    RandomAccessData entryData = this.entries.getEntryData(entry.getName());
    return new JarFile(this.rootFile, this.pathFromRoot + "!/" + entry.getName(),
                       entryData, JarFileType.NESTED_JAR);
}

到这基本上读取jar内部信息,加载为对应归档文件对象的大概过程已经讲完了,接下来分析一下在获取到了整个jar的归档文件对象后的处理。

通过归档文件对象列表,获取对应的url信息,并通过url信息创建LaunchedURLClassLoader

protected ClassLoader createClassLoader(List<Archive> archives) {
    List<URL> urls = new ArrayList<URL>(archives.size());
    for (Archive archive : archives) {
        urls.add(archive.getUrl());
    }
    return createClassLoader(urls.toArray(new URL[urls.size()]));
}

获取到对应的LaunchedUrlClassLoader类加载器之后,设置线程的上下文类加载器为该加载器。根据MANIFI.MF文件中的start-classs信息创建项目启动入口主类对象,并通过返回对象的run方法启动

protected void launch(String[] args, String mainClass, ClassLoader classLoader) {
    Thread.currentThread().setContextClassLoader(classLoader);
    createMainMethodRunner(mainClass, args, classLoader).run();
}

进入MainMethodRunnerrun方法:先通过当前线程获取到main入口类,然后通过反射调用启动项目启动类的main方法

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

最后来说一下这个LaunchedURLClassLoader,它继承于URLClassLoader,并重写了loadClass方法

LaunchedClassLoaderloadClass方法:调用父类loadClass方法,走正常委派流程,最终会被LaunchURLClassLoader加载。

@Override
protected Class<?> loadClass(String name, boolean resolve){
   try {
      try {
         definePackageIfNecessary(name);
      }
      return super.loadClass(name, resolve);
   }
}

进入URLClassLoader中根据springboot解析进行解析。根据名称将路径转化为以.class结尾的/分隔的格式。通过UrlClassPath对象根据路径获取资源类文件

new PrivilegedExceptionAction<Class<?>>() {
    public Class<?> run() throws ClassNotFoundException {
      String path = name.replace('.', '/').concat(".class");
      Resource res = ucp.getResource(path, false);
      if (res != null) {
        try {
          return defineClass(name, res);
        }
      }
    }
  }

四、总结

Springboot主要实现了对URL加载方式进行了扩展,并且对一些对象ArchiveJarFileEntry等进行了抽象和扩展,最后使用LaunchedUrlClassLoader来进行处理。

到此这篇关于SpringBoot的jar包如何启动的实现的文章就介绍到这了,更多相关SpringBoot jar包启动内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用springboot的jar包能够以service方式启动

    目录 springboot的jar包以service方式启动 场景 过程 打包(maven),授权,启动 建立软连接,通过service命令来启动 systemctl配置 Springboot以jar包方式启动.关闭.重启脚本 启动 关闭 重启 springboot的jar包以service方式启动 场景 打出的jar包用java -jar肯定是可以启动的. 这种方式原生简单,但是对运维不友好. 于是要求改造,希望可以用service命令来启动. 过程 技术上完全可以实现的. pom.xml配置

  • SpringBoot工程搭建打包、启动jar包和war包的教程图文详解

    工程搭建 1.File->new->project: 2.选择"Spring Initializr",点击next:(jdk1.8默认即可) 3.完善项目信息,组名可不做修改,项目名可做修改:最终建的项目名为:test,src->main->java下包名会是:com->example->test:点击next: 4.Web下勾选Spring Web Start,(网上创建springboot项目多是勾选Web选项,而较高版本的Springboot没

  • Linux编辑启动、停止与重启springboot jar包脚本实例

    前言 springboot的配置文件中,配置文件的名字都有各自的意义跟用途 dev 开发环境 prod 生产环境(默认) test 测试环境 加载指定配置文件 --spring.profiles.active=prod springboot加载jar包的方式有 // 直接在控制台进行启动,缺点就是控制台关闭项目也就关闭了. java -jar bootdo.jar // 这种方式可以运行在后台,但是如果推出了shell的话,那也会挂 java -jar /bootdo-2.0.0.jar > b

  • SpringBoot项目运行jar包启动的步骤流程解析

    SpringBoot项目在开发中,方便快捷,有一点原因就是SpringBoot项目可以打jar包运行:把jar包直接扔服务器上,然后运行jar包就能访问项目接口了.下面介绍SpringBoot项目打jar包运行的步骤流程: 一.我们所熟悉的是在开发环境下,直接用开发工具来运行那个启动类,然后就能启动这个项目: 开发环境下启动项目 二. SpringBoot项目打jar包方法: [1]在cmd界面中,进入项目的本地存储地址 cmd命令下进入项目地址 [2]运行maven的打包命令,mvn clea

  • SpringBoot的jar包如何启动的实现

    目录 一.简介 二.jar包的内部结构 三.加载过程 1.使用到的一些类 2.过程分析 四.总结 一.简介 ​ 使用过SprongBoot打过jar包的都应该知道,目标文件一般都会生成两个文件,一个是以.jar的包,一个是.jar.original文件.那么使用SpringBoot会打出两个包,而.jar.original的作用是什么呢?还有就是java -jar是如何将一个SpringBoot项目启动,之间都进行了那些的操作? ​ 其实.jar.original是maven在SpringBoo

  • 解决spring-boot 打成jar包后 启动时指定参数无效的问题

    spring-boot打成jar启动时指定参数无效 今天后台项目进行修改,使用spring.profiles来指定启动时使用的配置文件. 在项目中添加好配置文件后使用java -jar .\base-exec.jar --spring.profiles.active=dev --server.port=9121启动时参数注入不进去. 检查配置文件书写的规则,这里把规则说一下 我们在开发Spring Boot应用时,通常同一套程序会被应用和安装到几个不同的环境,比如:开发.测试.生产等.其中每个环

  • SpringBoot应用jar包启动原理详解

    目录 1.maven打包 2.Jar包目录结构 3.可执行Jar(JarLauncher) 4.WarLauncher 5.总结 1.maven打包 Spring Boot项目的pom.xml文件中默认使用spring-boot-maven-plugin插件进行打包: <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>s

  • springboot工程jar包部署到云服务器的方法

    首先,springboot在这里不多做介绍了,关于怎么打成jar包,我相信凡是用过的同学都知道吧! 然后就是将jar包上传到云服务器了,这个有好些工具的,博主在这里也不多做说明了! 咱们还是直奔主题吧.... 运行命令:java -jar xxx.jar,然后springboot工程的内置tomcat就开启了,项目已经启动,接下来你就可以访问了. 感觉一切ok,其实你已经掉到坑里了... 怎么回事呢? 当你关闭当前的xshell 命令界面时,再次访问就失效了,why? why? why? 这是因

  • SpringBoot项目Jar包如何瘦身部署的实现

    一.背景 SpringBoot 为我们快速开发提供了很好的架子,使得我们只需要少量配置就能开始我们的开发工作,但是当我们需要打包上传部署时,却是很神伤的一个问题,因为打出来的 Jar 包少则十几兆,多则一百来兆,我们需要上传至公网服务器时,是非常慢的,这就引出了今天的主题,SpringBoot项目Jar包如何瘦身部署 1.思路分析 jar,我们可以看出,jar 包里面分为以下三个模块 分为 BOOT-INF,META-INF,org 三个部分,打开 BOOT-INF 可以看到有 classes,

  • 使用SpringBoot打jar包并部署到Tomcat详细步骤

    详细步骤 首先在pom.xml文件中做一些修改: 之前打war包需要修改打包方式,这次不需要了,因为默认就是 jar 包指定最终打成jar包的名称手动指定 resources 文件夹编译打包的路径添加SpringBoot内嵌Tomcat解析jsp的依赖(仅仅是为这个实例而添加) <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/PO

  • SpringBoot解决jar包冲突的问题,简单有效

    目录 SpringBoot解决jar包冲突 今天SpringBoot项目打包发现一直在报错 我查到的一个解决方案,可使用所有jar包冲突问题 spring boot jar冲突问题集锦 1.日志jar包冲突 2.本地ok,测试环境失败之mainstay 3.本地ok,测试环境失败之servlet 4.本地ok,测试环境失败之tomcat 5.本地ok,测试环境失败之spring asm 6.万恶的测试环境字节码验证失败 7.日志不能正常输出问题 8.本地打包正常 SpringBoot解决jar包

  • SpringBoot打jar包遇到的xml文件丢失的解决方案

    目录 SpringBoot打jar包遇到的xml文件丢失 在pom.xml的build标签中添加如下内容 SpringBoot打jar包遇到的一些问题 1.访问不到jsp页面 1.1 jar包中没有jsp文件,报404错误 1.2 还是访问不到页面,但不报错,一直在加载 1.3 此时若还报错 SpringBoot打jar包遇到的xml文件丢失 在pom.xml的build标签中添加如下内容 指定资源路径 <resources> <resource> <directory>

  • 使用Springboot 打jar包实现分离依赖lib和配置

    目录 打jar包实现分离依赖lib和配置 操作步骤 Springboot中jar包分离(分离lib包) jar包分离介绍 jar包分离实施步骤 小结一下 打jar包实现分离依赖lib和配置 为了业务需要配置文件和jar分离,便于使用者修改配置信息,在网上找了很久,最终找到一个简单有效的方法. 操作步骤 打开project工程的pom.xml文件,将下列配置粘贴到pom文件中: <properties> <java.version>1.8</java.version> &

随机推荐