作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Igor是一位热情而熟练的专业人士,在Java开发方面拥有丰富的经验, system administration, and more.
本文将演示如何启动 Spring Boot 应用程序从另一个Java程序. Spring Boot应用程序通常被构建到单个可执行JAR归档文件中. 它包含了所有的依赖关系,打包成嵌套的jar.
Likewise, Spring Boot项目通常由提供的maven插件构建为可执行的JAR文件,该插件会完成所有繁琐的工作. 其结果是方便, 易于与他人共享的单个JAR文件, deploy on a server, and so on.
启动Spring Boot应用程序就像输入一样简单 java -jar mySpringProg.jar,应用程序将在控制台上打印一些格式良好的信息消息.
But what if a Spring Boot developer 希望在没有人工干预的情况下从另一个Java程序运行应用程序?
将带有所有依赖项的Java程序打包到单个可运行的JAR文件中, 必须提供同样是JAR文件的依赖项,并以某种方式存储在最终的可运行JAR文件中.
“Shading” is one option. 着色依赖关系是包含和重命名依赖关系的过程, relocating the classes, 并重写受影响的字节码和资源,以便创建与应用程序(项目)自己的代码捆绑在一起的副本.
Shading允许用户从依赖项中解包所有类和资源,并将它们打包回可运行的JAR文件中. 这可能适用于简单的场景, however, 如果两个依赖项包含具有完全相同名称和路径的相同资源文件或类, 它们会重叠,程序可能就不起作用了.
Spring Boot采用不同的方法,将依赖JAR打包到可运行的JAR中,作为嵌套JAR.
example.jar
|
+-META-INF
| +-MANIFEST.MF
+-org
| +-springframework
| +-boot
| +-loader
| +-
+-BOOT-INF
+-classes
| +-mycompany
| +-project
| +-YourClasses.class
+-lib
+-dependency1.jar
+-dependency2.jar
A JAR archive 组织为标准的java可运行JAR文件. Spring引导加载程序类位于 org/springframework/boot/loader
路径,而用户类和依赖项在 BOOT-INF/classes
and BOOT-INF/lib
.
Note: 如果你是Spring的新手,你可能也想看看我们的 十大最常见的Spring框架错误 article.
典型的Spring Boot JAR文件包含三种类型的条目:
Spring Boot Classloader将首先在类路径中设置JAR库,然后再设置项目类, 这使得从IDE运行Spring Boot应用程序(Eclipse, IntelliJ) and from console.
有关类重写和类装入器的其他信息,请参阅 this article.
从命令行或shell中手动启动Spring Boot应用程序很容易,只需输入以下命令:
java -jar example.jar
However, 从另一个Java程序以编程方式启动Spring Boot应用程序需要更多的工作. 有必要加载 org/springframework/boot/loader/ *.class
代码,使用一点Java反射来实例化 JarFileArchive
, JarLauncher
, and invoke the launch(String[])
method.
我们将在下面的部分中更详细地了解这是如何实现的.
正如我们已经指出的,Spring Boot JAR文件就像任何JAR归档文件一样. It is possible to load org/springframework/boot/loader/ *.class
条目,创建Class对象,然后使用它们启动Spring Boot应用程序.
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
. . .
loadJar(final String pathToJar)抛出IOException . . . {
//类名到类对象的映射.
final Map> classMap = new HashMap<>();
final JarFile JarFile = new JarFile(pathToJar);
final Enumeration jarEntryEnum = jarFile.entries();
final URL[] URL ={新URL("jar:file:" + pathToJar + ")!/") };
URLClassLoader = URLClassLoader . final.newInstance(urls);
Here we can see classMap
将保存映射到各自包名的类对象,例如.g., String value org.springframework.boot.loader.JarLauncher
will be mapped to the JarLauncher.class
object.
while (jarEntryEnum.hasMoreElements()) {
JarEntry = jarEntryEnum.nextElement();
if (jarEntry.getName().startsWith(“org/springframework/boot”)
&& jarEntry.getName().endsWith(".class") == true) {
int endIndex = jarEntryName.lastIndexOf(".class");
className = jarEntryName.substring(0, endIndex).replace('/', '.');
try {
final Class> loadedClass = urlClassLoader.loadClass(className);
result.put(loadedClass.getName (), loadedClass);
}
catch (final ClassNotFoundException ex) {
}
}
}
jarFile.close();
The end result of the while loop 是一个映射与Spring引导加载程序类对象填充.
把装载的东西移开, 我们可以继续完成自动启动并使用它来实际启动我们的应用程序.
Java反射允许从加载的类创建对象, 这在我们的教程中非常有用.
第一步是创建一个 JarFileArchive
object.
//创建JarFileArchive(File)对象,用于JarLauncher.
final Class> jarFileArchiveClass = result.get("org.springframework.boot.loader.archive.JarFileArchive");
final Constructor> jarFileArchiveConstructor =
jarFileArchiveClass.getConstructor(File.class);
最终对象jarFileArchive =
jarFileArchiveConstructor.newInstance(新文件(pathToJar));
The constructor of the JarFileArchive
object takes a File(String)
对象作为参数,因此必须提供它.
下一步是创建一个 JarLauncher
object, which requires Archive
in its constructor.
final Class> archiveClass = result.get("org.springframework.boot.loader.archive.Archive");
//使用JarLauncher(Archive)构造函数创建JarLauncher对象.
final Constructor> jarLauncherConstructor = mainClass.getDeclaredConstructor (archiveClass);
jarLauncherConstructor.setAccessible(true);
最终对象jarLauncher = jarLauncherConstructor.newInstance (jarFileArchive);
为了避免混淆,请注意 Archive
实际上是一个界面,而 JarFileArchive
是其中一个实现吗.
过程的最后一步是调用 launch(String[])
方法在新创建的 jarLauncher
object. 这相对简单,只需要几行代码.
//调用JarLauncher#launch(String[])方法.
final Class> launcherClass = result.get("org.springframework.boot.loader.Launcher");
launchMethod =
launcherClass.getDeclaredMethod(“发射”,String [].class);
launchMethod.setAccessible(true);
launchMethod.调用(jarLauncher, new Object[]{new String[0]});
The 调用(jarlauncher, new Object[]{new String[0]})
方法将最终启动Spring Boot应用程序. 注意,主线程将在这里停止并等待Spring Boot应用程序终止.
检查我们的Spring Boot JAR文件将揭示以下结构:
+--- mySpringApp1-0.0.1-SNAPSHOT.jar
+--- META-INF
+--- BOOT-INF
| +—classes # 1—项目类
| | |
| | +--- com.example.mySpringApp1
| | \——SpringBootLoaderApplication.class
| |
| +——lib # 2 -嵌套的jar库
| +--- javax.annotation-api-1.3.1
| +—spring-boot-2.0.0.M7.jar
| \--- (...)
|
+--- org.springframework.boot.loader # 3 - Spring引导加载程序类
+--- JarLauncher.class
+ - - - LaunchedURLClassLoader.class
\--- (...)
注意条目的三种类型:
Both project classes (BOOT-INF/classes
) and nested JARs (BOOT-INF/lib
)由同一个类装入器处理 LaunchedURLClassLoader
. 这个加载程序位于Spring Boot JAR应用程序的根目录中.
The LaunchedURLClassLoader
将加载类内容(BOOT-INF/classes
)后的图书馆内容(BOOT-INF/lib
),这与IDE不同. For example, Eclipse首先将类内容放在类路径中,然后是库(依赖项)。.
LaunchedURLClassLoader
extends java.net.URLClassLoader
,它是用一组将用于类加载的url创建的. URL可能指向JAR存档或类文件夹之类的位置. 在执行类加载时, url指定的所有资源都将按照提供url的顺序遍历, 并且将使用包含搜索类的第一个资源.
经典Java应用程序要求在classpath参数中枚举所有依赖项, 使启动过程有些繁琐和复杂.
相比之下,Spring Boot应用程序很方便,而且很容易从命令行启动. 它们管理所有的依赖关系,最终用户不需要担心细节.
However, 从另一个Java程序启动Spring Boot应用程序会使这个过程更加复杂, 因为它需要加载Spring Boot的加载器类, 创建专门的对象,例如 JarFileArchive
and JarLauncher
,然后使用Java反射来调用 launch
method.
Bottom lineSpring Boot可以在底层处理很多琐碎的任务, 允许开发人员腾出时间,专注于更有用的工作,如创建新功能, testing, and so on.
Spring Boot使独立创建变得很容易, production-grade, 易于运行或部署的基于spring的应用程序, 而Spring框架是一套全面的Java库,用于开发富web, desktop, or mobile applications.
Spring Boot提供maven模板, 内置Tomcat web服务器, 以及一些预定义的配置来简化Spring的使用. 大多数Spring Boot应用程序只需要很少的Spring配置. Spring Boot用于创建可以通过使用Java -jar或更传统的战争部署启动的Java应用程序.
Spring Boot架构提供启动器, auto-configuration, 和组件扫描,以便在不需要复杂的XML配置文件的情况下开始使用Spring.
Igor是一位热情而熟练的专业人士,在Java开发方面拥有丰富的经验, system administration, and more.
世界级的文章,每周发一次.
世界级的文章,每周发一次.
Join the Toptal® community.