




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第SpringBoot的jar包如何启动的实现目录一、简介二、jar包的内部结构三、加载过程1.使用到的一些类2.过程分析四、总结
一、简介
使用过SprongBoot打过jar包的都应该知道,目标文件一般都会生成两个文件,一个是以.jar的包,一个是.jar.original文件。那么使用SpringBoot会打出两个包,而.jar.original的作用是什么呢?还有就是java-jar是如何将一个SpringBoot项目启动,之间都进行了那些的操作?
其实.jar.original是maven在SpringBoot重新打包之前的原始jar包,内部只包含了项目的用户类,不包含其他的依赖jar包,生成之后,SpringBoot重新打包之后,最后生成.jar包,内部包含了原始jar包以及其他的引用依赖。以下提及的jar包都是SpringBoot二次加工打的包。
二、jar包的内部结构
SpringBoot打出的jar包,可以直接通过解压的方式查看内部的构造。一般情况下有三个目录。
BOOT-INF:这个文件夹下有两个文件夹classes用来存放用户类,也就是原始jar.original里的类;还有一个是lib,就是这个原始jar.original引用的依赖。META-INF:这里是通过java-jar启动的入口信息,记录了入口类的位置等信息。org:Springbootloader的代码,通过它来启动。
这里主要介绍一下/BOOT-INF/MANIFEST.MF文件
Main-Class:org.springframework.boot.loader.JarLauncher
Start-Class:.springboot.center.AuthEenterBootstrap
Main-Class:记录了java-jar的启动入口,当使用该命令启动时就会调用这个入口类的main方法,显然可以看出,Springboot转移了启动的入口,不是用户编写的xxx.xxx.BootStrap的那个入口类。
Start-Class:记录了用户编写的xxx.xxx.BootStrap的那个入口类,当内嵌的jar包加载完成之后,会使用LaunchedURLClassLoader线程加载类来加载这个用户编写的入口类。
三、加载过程
1.使用到的一些类
3.1.1Archive
归档文件接口,实现迭代器接口,它有两个子类,一个是JarFileArchive对jar包文件使用,提供了返回这个jar文件对应的url、或者这个jar文件的MANIFEST文件数据信息等操作。是ExplodedArchive是文件目录的使用也有获取这个目录url的方法,以及获取这个目录下的所有Archive文件方法。
3.1.2Launcher
启动程序的基类,这边最后是通过JarLauncher#main()来启动。ExecutableArchiveLauncher是抽象类,提供了获取Start-Class类路径的方法,以及是否还有内嵌对应文件的判断方法和获取到内嵌对应文件集合的后置处理方法的抽象,由子类JarLauncher和WarLauncher自行实现。
3.1.3Spring.loader下的JarFile和JarEntry
jarFile继承于jar.util.jar.JarFile,JarEntry继承于java.util.jar.JarEntry,对原始的一些方法进行重写覆盖。每一个JarFileArchive都拥有一个JarFile方法,用于存储这个jar包对应的文件,而每一个JarFile都有一个JarFileEntries,JarFileEntries是一个迭代器。总的来说,在解析jar包时,会将jar包内的文件封装成JarEntry对象后由JarFile对象保存文件列表的迭代器。所以JarFileArchive和JarFileEntries之间是通过JarFile连接,二者都可以获取到JarFile对象。
2.过程分析
从MANIFEST.MF文件中的Main-class指向入口开始。
创建JarLauncher并且通过它的launch()方法开始加载jar包内部信息。
publicstaticvoidmain(String[]args)throwsException{
newJarLauncher().launch(args);
}
JarLauncher的空构造方法时一个空实现,刚开始看的时候还懵了一下,以为是在后续的操作中去加载的文件,其实不然,在创建时由父类ExecutableArchiveLauncher的构造方法去加载的文件。
加载为归档文件对象:
this.archive=createArchive();
具体的加载方法:判断路径是否是一个文件夹,是则返回ExplodedArchive对象,否则返回JarFileArchive进入JarFileArchive类:通过这个new方法创建JarFile对象
publicclassJarFileArchiveimplementsArchive{
publicJarFileArchive(Filefile,URLurl)throwsIOException{
this(newJarFile(file));
this.url=url;
}
进入到JarFile方法:通过RandomAccessDataFile读取文件的内容,并传递给本类中的方法进行具体的解析。
publicclassJarFileextendsjava.util.jar.JarFile{
publicJarFile(Filefile)throwsIOException{
this(newRandomAccessDataFile(file));
}
进入jarLauncher的launch方法:注册URL协议的处理器,没有指定时,默认指向org.springframework.boot.loader包路径,获取类路径下的归档文件Archive并通过这些归档文件的URL,创建线程上下文类加载器,使用类加载器和用户编写的启动入口类,通过反射调用它的main方法。
protectedvoidlaunch(String[]args)throwsException{
JarFile.registerUrlProtocolHandler();
ClassLoaderclassLoader=createClassLoader(getClassPathArchives());
launch(args,getMainClass(),classLoader);
}
JarLauncher的getClassPathArchives是在ExecutableArchiveLauncher中实现:获取归档文件中满足EntryFilterg过滤器的项,isNestedArchive方法由具体的之类实现。获取到当前归档文件下的所有子归档文件之后的后置操作,是一个扩展点。在JarLauncher中是一个空实现。
JarLauncher的具体实现,这里通过判断是否在BOOT-INF/lib/包下返回true也就是说只会把jar包下的BOOT-INF/lib/下的文件加载为Archive对象
protectedbooleanisNestedArchive(Archive.Entryentry){
if(entry.isDirectory()){
returnentry.getName().equals(BOOT_INF_CLASSES);
returnentry.getName().startsWith(BOOT_INF_LIB);
}
JarFileArchive的getNestedArchives方法:若匹配器匹配到则获取内嵌归档文件。
具体的获取内嵌归档文件逻辑:根据具体的Entry对象,创建JarFile对象并封装成归档文件对象后返回。
protectedArchivegetNestedArchive(Entryentry)throwsIOException{
try{
JarFilejarFile=this.jarFile.getNestedJarFile(jarEntry);
returnnewJarFileArchive(jarFile);
}
获取到参数entry对应的RandomAccessData对象,这里根据springboot扩展的url协议,在父路径的基础上添加!/来标记子包。
privateJarFilecreateJarFileFromFileEntry(JarEntryentry)throwsIOException{
RandomAccessDataentryData=this.entries.getEntryData(entry.getName());
returnnewJarFile(this.rootFile,this.pathFromRoot+"!/"+entry.getName(),
entryData,JarFileType.NESTED_JAR);
}
到这基本上读取jar内部信息,加载为对应归档文件对象的大概过程已经讲完了,接下来分析一下在获取到了整个jar的归档文件对象后的处理。
通过归档文件对象列表,获取对应的url信息,并通过url信息创建LaunchedURLClassLoader
protectedClassLoadercreateClassLoader(ListArchivearchives){
ListURLurls=newArrayListURL(archives.size());
for(Archivearchive:archives){
urls.add(archive.getUrl());
returncreateClassLoader(urls.toArray(newURL[urls.size()]));
}
获取到对应的LaunchedUrlClassLoader类加载器之后,设置线程的上下文类加载器为该加载器。根据MANIFI.MF文件中的start-classs信息创建项目启动入口主类对象,并通过返回对象的run方法启动
protectedvoidlaunch(String[]args,StringmainClass,ClassLoaderclassLoader){
Thread.currentThread().setContextClassLoader(classLoader);
createMainMethodRunner(mainClass,args,classLoader).run();
}
进入MainMethodRunner的run方法:先通过当前线程获取到main入口类,然后通过反射调用启动项目启动类的main方法
publicvoidrun()throwsException{
ClassmainClass=Thread.currentThread().getContextClassLoader()
.loadClass(this.mainClassName);
MethodmainMethod=mainClass.getDeclaredMethod("main",String[].class);
mainMethod.invoke(null,newObject[]{this.args});
}
最后来说一下这个LaunchedURLClassLoader,它继承于URLClassLoader,并重写了loadClass方法
LaunchedClassLoader的loadClass方法:调用父类loadClass方法,走正常委派流程,最终会被LaunchURLClassLoader加载。
@Override
protectedClassloadClass(Stringname,booleanresolve){
try{
try{
definePackageIfNecessary(name);
returnsuper.loadClass(name,resolve);
}
进入URLClassLoader中根据springboot解析进行解析。根据名称将路径转化为以.class结尾的/分隔的格式。通过UrlClassPath对象根据路径获取资源类文件
newPrivilegedExceptionActionClass(){
publicClassrun()throwsClassNotFoundExcep
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论