SpringBoot java-jar命令行启动原理解析

在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启动的,而不需要另外配置一个Web Server。那么spring boot如何启动的呢?今天我们就来一起探究一下它的原理。首先我们来创建一个基本的spring boot工程来帮助我们分析,本次spring boot版本为 2.2.5.RELEASE。

// SpringBootDemo.java
@SpringBootApplication
public class SpringBootDemo {
 public static void main(String[] args) {
 SpringApplication.run(SpringBootDemo.class);
 }

}

下面是pom依赖:

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
</dependencies>

<build>
 <finalName>springboot-demo</finalName>
 <plugins>
 <plugin>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-maven-plugin</artifactId>
 </plugin>
 </plugins>
</build>

创建完工程后,执行maven的打包命令,会生成两个jar文件:

springboot-demo.jar
springboot-demo.jar.original

其中springboot-demo.jar.original是默认的maven-jar-plugin生成的包。springboot-demo.jar是spring boot maven插件生成的jar包,里面包含了应用的依赖,以及spring boot相关的类。下面称之为executable jar或者fat jar。后者仅包含应用编译后的本地资源,而前者引入了相关的第三方依赖,这点从文件大小也能看出。

图1

关于executable jar,spring boot官方文档中是这样解释的。

Executable jars (sometimes called “fat jars”) are archives containing your compiled classes along with all of the jar dependencies that your code needs to run.

Executable jar(有时称为“fat jars”)是包含您的已编译类以及代码需要运行的所有jar依赖项的归档文件。

Java does not provide any standard way to load nested jar files (that is, jar files that are themselves contained within a jar). This can be problematic if you need to distribute a self-contained application that can be run from the command line without unpacking.

Java没有提供任何标准的方式来加载嵌套的jar文件(即,它们本身包含在jar中的jar文件)。如果您需要分发一个自包含的应用程序,而该应用程序可以从命令行运行而无需解压缩,则可能会出现问题。

To solve this problem, many developers use “shaded” jars. A shaded jar packages all classes, from all jars, into a single “uber jar”. The problem with shaded jars is that it becomes hard to see which libraries are actually in your application. It can also be problematic if the same filename is used (but with different content) in multiple jars.

为了解决这个问题,许多开发人员使用 shaded jars。 一个 shaded jar 将来自所有jar的所有类打包到一个 uber(超级)jar 中。 shaded jars的问题在于,很难查看应用程序中实际包含哪些库。 如果在多个jar中使用相同的文件名(但具有不同的内容),也可能会产生问题。

Spring Boot takes a different approach and lets you actually nest jars directly.

Spring Boot采用了另一种方法,实际上允许您直接嵌套jar。

简单来说,Java标准中是没有来加载嵌套的jar文件,就是jar中的jar的方式的,为了解决这一问题,很多开发人员采用shaded jars,但是这种方式会有一些问题,而spring boot采用了不同于shaded jars的另一种方式。

Executable Jar 文件结构

那么spring boot具体是如何实现的呢?带着这个疑问,先来查看spring boot打好的包的目录结构(不重要的省略掉):

图6

可以发现,文件目录遵循了下面的规范:

Application classes should be placed in a nestedBOOT-INF/classesdirectory. Dependencies should be placed in a nested BOOT-INF/libdirectory.

应用程序类应该放在嵌套的BOOT-INF/classes目录中。依赖项应该放在嵌套的BOOT-INF/lib目录中。

我们通常在服务器中使用java -jar命令启动我们的应用程序,在Java官方文档是这样描述的:

Executes a program encapsulated in a JAR file. The filename argument is the name of a JAR file with a manifest that contains a line in the form Main-Class:classname that defines the class with the public static void main(String[] args) method that serves as your application's starting point.

执行封装在JAR文件中的程序。filename参数是具有清单的JAR文件的名称,该清单包含Main-Class:classname形式的行,该行使用公共静态void main(String [] args)方法定义该类,该方法充当应用程序的起点。

When you use the -jar option, the specified JAR file is the source of all user classes, and other class path settings are ignored.

使用-jar选项时,指定的JAR文件是所有用户类的源,而其他类路径设置将被忽略。

简单说就是,java -jar 命令引导的具体启动类必须配置在清单文件 MANIFEST.MF 的 Main-Class 属性中,该命令用来引导标准可执行的jar文件,读取的是 MANIFEST.MF文件的Main-Class 属性值,Main-Class 也就是定义包含了main方法的类代表了应用程序执行入口类。

那么回过头再去看下之前打包好、解压之后的文件目录,找到 /META-INF/MANIFEST.MF 文件,看下元数据:

Manifest-Version: 1.0 Implementation-Title: spring-boot-demo Implementation-Version: 1.0-SNAPSHOT Start-Class: com.example.spring.boot.demo.SpringBootDemo Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Build-Jdk-Spec: 1.8 Spring-Boot-Version: 2.2.5.RELEASE Created-By: Maven Archiver 3.4.0 Main-Class: org.springframework.boot.loader.JarLauncher

可以看到Main-Class是org.springframework.boot.loader.JarLauncher,说明项目的启动入口并不是我们自己定义的启动类,而是JarLauncher。而我们自己的项目引导类com.example.spring.boot.demo.SpringBootDemo,定义在了Start-Class属性中,这个属性并不是Java标准的MANIFEST.MF文件属性。

spring-boot-maven-plugin 打包过程

我们并没有添加org.springframework.boot.loader下的这些类的依赖,那么它们是如何被打包在 FatJar 里面的呢?这就必须要提到spring-boot-maven-plugin插件的工作机制了 。对于每个新建的 spring boot工程,可以在其 pom.xml 文件中看到如下插件:

<build>
 <plugins>
 <plugin>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-maven-plugin</artifactId>
 </plugin>
 </plugins>
</build>

这个是 SpringBoot 官方提供的用于打包 FatJar 的插件,org.springframework.boot.loader 下的类其实就是通过这个插件打进去的;

当我们执行package命令的时候会看到下面这样的日志:

[INFO] --- spring-boot-maven-plugin:2.2.5.RELEASE:repackage (repackage) @ spring-boot-demo ---
[INFO] Replacing main artifact with repackaged archive

repackage目标对应的将执行到org.springframework.boot.maven.RepackageMojo#execute,该方法的主要逻辑是调用了org.springframework.boot.maven.RepackageMojo#repackage

// RepackageMojo.java
private void repackage() throws MojoExecutionException {
 // 获取使用maven-jar-plugin生成的jar,最终的命名将加上.orignal后缀
 Artifact source = getSourceArtifact();
 // 最终文件,即Fat jar
 File target = getTargetFile();
 // 获取重新打包器,将重新打包成可执行jar文件
 Repackager repackager = getRepackager(source.getFile());
 // 查找并过滤项目运行时依赖的jar
 Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(), getFilters(getAdditionalFilters()));
 // 将artifacts转换成libraries
 Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack, getLog());
 try {
 // 提供Spring Boot启动脚本
 LaunchScript launchScript = getLaunchScript();
 // 执行重新打包逻辑,生成最后fat jar
 repackager.repackage(target, libraries, launchScript);
 }
 catch (IOException ex) {
 throw new MojoExecutionException(ex.getMessage(), ex);
 }
 // 将source更新成 xxx.jar.orignal文件
 updateArtifact(source, target, repackager.getBackupFile());
}

// 继续跟踪getRepackager这个方法,知道Repackager是如何生成的,也就大致能够推测出内在的打包逻辑。
private Repackager getRepackager(File source) {
 Repackager repackager = new Repackager(source, this.layoutFactory);
 repackager.addMainClassTimeoutWarningListener(new LoggingMainClassTimeoutWarningListener());
 // 设置main class的名称,如果不指定的话则会查找第一个包含main方法的类,
 // repacke最后将会设置org.springframework.boot.loader.JarLauncher
 repackager.setMainClass(this.mainClass);
 if (this.layout != null) {
 getLog().info("Layout: " + this.layout);
 repackager.setLayout(this.layout.layout());
 }
 return repackager;
}

repackager设置了 layout方法的返回对象,也就是org.springframework.boot.loader.tools.Layouts.Jar

/**
 * Executable JAR layout.
 */
public static class Jar implements RepackagingLayout {

 @Override
 public String getLauncherClassName() {
 return "org.springframework.boot.loader.JarLauncher";
 }

 @Override
 public String getLibraryDestination(String libraryName, LibraryScope scope) {
 return "BOOT-INF/lib/";
 }

 @Override
 public String getClassesLocation() {
 return "";
 }

 @Override
 public String getRepackagedClassesLocation() {
 return "BOOT-INF/classes/";
 }

 @Override
 public boolean isExecutable() {
 return true;
 }

}

layout我们可以将之翻译为文件布局,或者目录布局,代码一看清晰明了,同时我们又发现了定义在MANIFEST.MF 文件的Main-Class属性org.springframework.boot.loader.JarLauncher了,看来我们的下面的重点就是研究一下这个JarLauncher了。

JarLauncher构造过程

因为org.springframework.boot.loader.JarLauncher的类是在spring-boot-loader中的,关于spring-boot-loader,spring boot的github上是这样介绍的:

Spring Boot Loader provides the secret sauce that allows you to build a single jar file that can be launched usingjava -jar. Generally you will not need to use spring-boot-loaderdirectly, but instead work with the Gradle or  Mavenplugin.

Spring Boot Loader提供了秘密工具,可让您构建可以使用java -jar启动的单个jar文件。通常,您不需要直接使用spring-boot-loader,而可以使用Gradle或Maven插件。

但是若想在IDEA中来看源码,需要在pom文件中引入如下配置:

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

找到org.springframework.boot.loader.JarLauncher类

// JarLauncher.java
public class JarLauncher extends ExecutableArchiveLauncher {

 // BOOT-INF/classes/
 static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
 // BOOT-INF/lib/
 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);
 }
 // main方法
 public static void main(String[] args) throws Exception {
 new JarLauncher().launch(args);
 }

}

可以发现,JarLauncher定义了BOOT_INF_CLASSES和BOOT_INF_LIB两个常量,正好就是前面我们解压之后的两个文件目录。JarLauncher包含了一个main方法,作为应用的启动入口。

从 main 来看,只是构造了一个 JarLauncher对象,然后执行其 launch 方法 。再来看一下JarLauncher的继承结构:

图2

构造JarLauncherd对象时会调用父类ExecutableArchiveLauncher的构造方法:

// ExecutableArchiveLauncher.java
public ExecutableArchiveLauncher() {
 try {
 // 构造 archive 对象
 this.archive = createArchive();
 }
 catch (Exception ex) {
 throw new IllegalStateException(ex);
 }
}
// 构造 archive 对象
protected final Archive createArchive() throws Exception {
 ProtectionDomain protectionDomain = getClass().getProtectionDomain();
 CodeSource codeSource = protectionDomain.getCodeSource();
 URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null;
 // 这里就是拿到当前的 classpath 的绝对路径
 String path = (location != null) ? location.getSchemeSpecificPart() : null;
 if (path == null) {
 throw new IllegalStateException("Unable to determine code source archive");
 }
 File root = new File(path);
 if (!root.exists()) {
 throw new IllegalStateException("Unable to determine code source archive from " + root);
 }
 // 将构造的archive 对象返回
 return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));
}

Archive

这里又需要我们先来了解一下Archive相关的概念。

  • archive即归档文件,这个概念在linux下比较常见
  • 通常就是一个tar/zip格式的压缩包
  • jar是zip格式
public abstract class Archive {
 public abstract URL getUrl();
 public String getMainClass();
 public abstract Collection<Entry> getEntries();
 public abstract List<Archive> getNestedArchives(EntryFilter filter);
}

Archive是在spring boot里抽象出来的用来统一访问资源的接口。该接口有两个实现,分别是ExplodedArchive和JarFileArchive。前者是一个文件目录,后者是一个jar,都是用来在文件目录和jar中寻找资源的,这里看到JarLauncher既支持jar启动,也支持文件系统启动,实际上我们在解压后的文件目录里执行 java org.springframework.boot.loader.JarLauncher 命令也是可以正常启动的。

图3

在FatJar中,使用的是后者。Archive都有一个自己的URL,比如

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!

Archive类还有一个getNestedArchives方法,下面还会用到这个方法,这个方法实际返回的是springboot-demo.jar/lib下面的jar的Archive列表。它们的URL是:

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-starter-web-2.2.5.RELEASE.jar!

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-starter-2.2.5.RELEASE.jar!

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-2.2.5.RELEASE.jar!

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-autoconfigure-2.2.5.RELEASE.jar!/

省略......

launch()执行流程

archive构造完成后就该执行JarLauncher的launch方法了,这个方法定义在了父类的Launcher里:

// Launcher.java
protected void launch(String[] args) throws Exception {
 /*
 * 利用 java.net.URLStreamHandler 的扩展机制注册了SpringBoot的自定义的可以解析嵌套jar的协议。
 * 因为SpringBoot FatJar除包含传统Java Jar中的资源外还包含依赖的第三方Jar文件
 * 当SpringBoot FatJar被java -jar命令引导时,其内部的Jar文件是无法被JDK的默认实现
 * sun.net.www.protocol.jar.Handler当做classpath的,这就是SpringBoot的自定义协议的原因。
 */
 JarFile.registerUrlProtocolHandler();
 // 通过 classpath 来构建一个 ClassLoader
 ClassLoader classLoader = createClassLoader(getClassPathArchives()); // 1
 launch(args, getMainClass(), classLoader); // 2
}

重点关注下createClassLoader(getClassPathArchives()) 构建ClassLoader的逻辑,首先调用getClassPathArchives()方法返回值作为参数,该方法为抽象方法,具体实现在子类ExecutableArchiveLauncher中:

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

该方法会执行Archive接口定义的getNestedArchives方法返回的与指定过滤器匹配的条目的嵌套存档列表。从上文可以发现,这里的archive其实就是JarFileArchive ,传入的过滤器是JarLauncher#isNestedArchive方法引用

// JarLauncher.java
@Override
protected boolean isNestedArchive(Archive.Entry entry) {
 // entry是文件目录时,必须是我们自己的业务类所在的目录 BOOT-INF/classes/
 if (entry.isDirectory()) {
 return entry.getName().equals(BOOT_INF_CLASSES);
 }
 // entry是Jar文件时,需要在依赖的文件目录 BOOT-INF/lib/下面
 return entry.getName().startsWith(BOOT_INF_LIB);
}

getClassPathArchives方法通过过滤器将BOOT-INF/classes/和BOOT-INF/lib/下的嵌套存档作为List<Archive>返回参数传入createClassLoader方法中。

// Launcher.java
protected ClassLoader createClassLoader(List<Archive> archives) throws Exception {
 List<URL> urls = new ArrayList<>(archives.size());
 for (Archive archive : archives) {
 // 前面说到,archive有一个自己的URL的,获得archive的URL放到list中
 urls.add(archive.getUrl());
 }
 // 调用下面的重载方法
 return createClassLoader(urls.toArray(new URL[0]));
}

// Launcher.java
protected ClassLoader createClassLoader(URL[] urls) throws Exception {
 return new LaunchedURLClassLoader(urls, getClass().getClassLoader());
}

createClassLoader()方法目的是为得到的URL们创建一个类加载器 LaunchedURLClassLoader,构造时传入了当前Launcher的类加载器作为其父加载器,通常是系统类加载器。下面重点看一下LaunchedURLClassLoader的构造过程:

// LaunchedURLClassLoader.java
public LaunchedURLClassLoader(URL[] urls, ClassLoader parent) {
 super(urls, parent);
}

LaunchedURLClassLoader是spring boot自己定义的类加载器,继承了JDK的URLClassLoader并重写了loadClass方法,也就是说它修改了默认的类加载方式,定义了自己的类加载规则,可以从前面得到的 List<Archive>中加载依赖包的class文件了 。

LaunchedURLClassLoader创建完成后,我们回到Launcher中,下一步就是执行launch的重载方法了。

// Launcher.java
launch(args, getMainClass(), classLoader);

在此之前,会调用getMainClass方法并将其返回值作为参数。

getMainClass的实现在Launcher的子类ExecutableArchiveLauncher中:

// ExecutableArchiveLauncher.java
@Override
protected String getMainClass() throws Exception {
 // 从 archive 中拿到 Manifest文件
 Manifest manifest = this.archive.getManifest();
 String mainClass = null;
 if (manifest != null) {
 // 就是MANIFEST.MF 文件中定义的Start-Class属性,也就是我们自己写的com.example.spring.boot.demo.SpringBootDemo这个类
 mainClass = manifest.getMainAttributes().getValue("Start-Class");
 }
 if (mainClass == null) {
 throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
 }
 // 返回mainClass
 return mainClass;
}

得到mainClass后,执行launch的重载方法:

// Launcher.java
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
 // 将自定义的LaunchedURLClassLoader设置为当前线程上下文类加载器
 Thread.currentThread().setContextClassLoader(classLoader);
 // 构建一个 MainMethodRunner 实例对象来启动应用
 createMainMethodRunner(mainClass, args, classLoader).run();
}

// Launcher.java
protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
 return new MainMethodRunner(mainClass, args);
}

MainMethodRunner对象构建完成后,调用它的run方法:

// MainMethodRunner.java
public void run() throws Exception {
 // 使用当前线程上下文类加载器也就是自定义的LaunchedURLClassLoader来加载我们自己写的com.example.spring.boot.demo.SpringBootDemo这个类
 Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
 // 找到SpringBootDemo的main方法
 Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
 // 最后,通过反射的方式调用main方法
 mainMethod.invoke(null, new Object[] { this.args });
}

至此,我们自己的main方法开始被调用,所有我们自己的应用程序类文件均可通过/BOOT-INF/classes加载,所有依赖的第三方jar均可通过/BOOT-INF/lib加载,然后就开始了spring boot的启动流程了。

debug技巧

以上就是spring boot通过java -jar命令启动的原理了,了解了原理以后我们可不可以通过debug来进一步加深一下理解呢?通常我们在IDEA里启动时是直接运行main方法,因为依赖的Jar都让IDEA放到classpath里了,所以spring boot直接启动就完事了,并不会通过上面的方式来启动。不过我们可以通过配置IDEA的 run/debug configurations 配置 JAR Application 来实现通过Jar方式启动。

图4

当我们做了以上设置后,就可以来方便的在IDEA里来dubug源码了。

图5小结

本文通过JarLauncher为切入点,介绍了spring boot的java -jar的启动方式,阐述了JarLauncher启动的基本工作原理,同时简单介绍了相关的spring-boot-maven-plugin插件和Archive、LaunchedURLClassLoader等相关概念,希望能够对大家的理解有所帮助。

到此这篇关于SpringBoot java-jar命令行启动原理解析的文章就介绍到这了,更多相关SpringBoot java-jar命令行启动内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springboot命令行启动的方法详解

    springboot命令行启动 创建的springboot项目想看看效果,不想打开idea等开发工具,使用直接使用命令行启动. maven的命令启动 需要将 jdk的bin目录和maven的bin目录添加到环境变量path中,若是没有,mvn就要用在maven的bin环境中的全路径 若是没有添加环境变量 mvn就要是E:\software\apache-maven-3.3.9\bin\mvn(安装路径\bin\mvn) java就要是C:\software\jdk\bin\java.exe(安装

  • Spring Boot命令行运行器的实现方法

    CommandLineRunner是一个带有run方法的简单spring引导接口.Spring Boot启动后将自动调用实现CommandLineRunner接口的所有bean的run方法. Command Line Runner在加载应用程序上下文之后以及Spring Application run方法完成之前执行,相当于你的应用的初始化过程,一般用来实现一些数据预先加载或预先处理. @SpringBootApplication <b>public</b> <b>cl

  • 如何使用Spring Boot ApplicationRunner解析命令行中的参数

    使用Spring提供的CommandLineRunner接口可以实现了一个命令行应用程序.但是,参数/选项/参数处理却不是那么好.幸运的是,有一种更好的方法可以使用Spring Boot编写命令行应用程序,并且还可以使用ApplicationRunner接口进行解析. 在我们开始快速说明之前.在这两种情况下,无论是CommandLineRunner还是ApplicationRunner,都始终支持Spring的属性处理.我们可以像往常一样使用@Value注释注入值. 完整的工作源代码在这里 首先

  • spring boot 命令行启动的方式

    在使用spring boot 构建应用启动时,我们在工作中都是通过命令行来启动应用,有时候会需要一些特定的参数以在应用启动时,做一些初始化的操作. spring boot 提供了 CommandLineRunner 和 ApplicationRunner 这两个接口供用户使用. 1. CommandLineRunner 1.1 声明: @FunctionalInterface public interface CommandLineRunner { /** * Callback used to

  • SpringBoot java-jar命令行启动原理解析

    在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启动的,而不需要另外配置一个Web Server.那么spring boot如何启动的呢?今天我们就来一起探究一下它的原理.首先我们来创建一个基本的spring boot工程来帮助我们分析,本次spring boot版本为 2.2.5.RELEASE. // SpringBootDemo.java @SpringBootApplication public class Spr

  • Spring Boot 的java -jar命令启动原理详解

    导语 在运用Spring Boot 后,我们基本上摆脱之前项目每次上线的时候把项目打成war包.当然也不排除一些奇葩的规定,必须要用war包上线,不过很多时候,我们对一些东西只是处在使用的阶段,并不会去深入的研究使用的原理是什么,这貌似也是大多数人的固定思维. 或许正是如此,总会有些没有固定思维的人会去积极的探索原理,当然这话不是说我是积极的,我其实也是只原理的搬运工.今天和大家来简单的说下Spring Boot 的项目在运行Java -jar的原理. jar包目录和jar命令启动入口 在正式开

  • spring boot jar的启动原理解析

     1.前言 近来有空对公司的open api平台进行了些优化,然后在打出jar包的时候,突然想到以前都是对spring boot使用很熟练,但是从来都不知道spring boot打出的jar的启动原理,然后这回将jar解开了看了下,与想象中确实大不一样,以下就是对解压出来的jar的完整分析. 2.jar的结构 spring boot的应用程序就不贴出来了,一个较简单的demo打出的结构都是类似,另外我采用的spring boot的版本为1.4.1.RELEASE网上有另外一篇文章对spring

  • SpringBoot扩展外部化配置的原理解析

    Environment实现原理 在基于SpringBoot开发的应用中,我们常常会在application.properties.application-xxx.properties.application.yml.application-xxx.yml等配置文件中设置一些属性值,然后通过@Value.@ConfigurationProperties等注解获取,或者采用编码的方式通过Environment获取. # application.properties my.config.appId=d

  • 基于spring boot 命令行启动的一些坑

    目录 spring boot 命令行启动的一些坑 1.spring boot项目启动时可以指定启动的参数 2.使用–spring.profiles.active=test,无论如何都没办法修改环境 spring-boot命令行启动(指定端口启动) springboot指定端口的三种方式 方法一 方法二 spring boot 命令行启动的一些坑 1.spring boot项目启动时可以指定启动的参数 例如: java -jar Project.jar --spring.profiles.acti

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

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

  • springboot加载命令行参数ApplicationArguments的实现

    目录 一.介绍 二.通过应用程序参数获取配置 1. 通过bean获取应用程序参数 2. 通过@Value注解获取 三.源码解读 - 封装应用程序参数 1. DefaultApplicationArguments 2. Source类 3. SimpleCommandLinePropertySource 4. SimpleCommandLineArgsParser 5. CommandLinePropertySource 6. PropertySource 四.源码解读 - 为什么可以通过@Val

  • MAC 命令行启动tomcat的详细介绍

    MAC 命令行启动tomcat的详细介绍 一.修改授权 进入tomcat的bin目录,修改授权 ➜ bin pwd /Users/yp/Documents/workspace/apache-tomcat-7.0.68/bin ➜ bin sudo chmod 755 *.sh sudo为系统超级管理员权限. chmod 改变一个或多个文件的存取模式 755代表用户对该文件拥有读.写.执行的权限,同组的其他人员拥有执行和读的权限,没有写的权限,其它用户的权限和同组人员一样. 777代表,user,

  • 浅谈SpringBoot内嵌Tomcat的实现原理解析

    一.序言 使用SpringBoot经常会使用内嵌的tomcat做为项目的启动容器,本文将从源码的角度出发,剖析SpringBoot内嵌Tomcat的实现原理,讨论Tomcat何时创建.何时启动以及怎么启动. 二.引入Tomcat组件 导入依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId&

随机推荐