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
的实现类为:JarLauncher
和WarLauncher
。
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);