安庆大理运城常德铜陵江西
投稿投诉
江西南阳
嘉兴昆明
铜陵滨州
广东西昌
常德梅州
兰州阳江
运城金华
广西萍乡
大理重庆
诸暨泉州
安庆南充
武汉辽宁

从原理和源码梳理SpringbootFatJar的机制

4月24日 小米粒投稿
  一、概述
  SpringBootFatJar的设计,打破了标准jar的结构,在jar包内携带了其所依赖的jar包,通过jar中的main方法创建自己的类加载器,来识别加载运行其不规范的目录下的代码和依赖。二、标准的jar包结构
  打开Java的Jar文件我们经常可以看到文件中包含着一个METAINF目录,这个目录下会有一些文件,其中必有一个MANIFEST。MF,这个文件描述了该Jar文件的很多信息其中MainClass定义Jar文件的入口类,该类必须是一个可执行的类,一旦定义了该属性即可通过javajarxxx。jar来运行该jar文件。
  在生产环境是使用javajarxxx。jar的方式来运行SpringBoot程序。这种情况下,SpringBoot应用真实的启动类并不是我们所定义的带有main方法的类,而是JarLauncher类。查看SpringBoot所打成的FatJar,其MainClass是org。springframework。boot。loader。JarLauncher,这便是微妙之处。SpringBootVersion:2。1。3。RELEASEMainClass:org。springframework。boot。loader。JarLauncherStartClass:com。rock。springbootlearn。SpringbootLearnApplicationSpringBootClasses:BOOTINFclassesSpringBootLib:BOOTINFlibBuildJdk:1。8。0131复制代码
  JAR包中的MANIFEST。MF文件详解以及编写规范三、探索JarLauncher
  org。springframework。boot。loader。JarLauncher这个类是哪里来的呢?答案在springbootloader。jar包中,可找到这个JarLauncher类的源码。在项目中加入maven依赖,以便查看源码和远程调试。dependencygroupIdorg。springframework。bootgroupIdspringbootloaderartifactIddependency复制代码
  认真比较可以看出,这个springbootloader包中的内容与SpringBoot的FatJar包中的一部分内容几乎一样。JarLauncher在jar中的位置如下:
  3。1只能拷贝出来一份儿
  重点重点重点:因jar规范要求MainClass所指定的类必须位于jar包的顶层目录下,即org。springframework。boot。loader。JarLauncher这个org必须位于jar包中的第一级目录,不能放置在其他的目录下。所以所以所以只能将springbootloader这个jar包的内容拷贝出来,而不是整个jar直接放置于执行Jar中。3。2携带程序所依赖的jar而非仅class
  上边JarLauncher的这个org。springframework。xx以及METAINF这两个目录是符合jar包规范的。但是BOOTINF这个目录里边有点像我们开发中的一些用法:依赖jar包在lib目录下但按照jar包规范jar中不能有jar包的情况下程序。class文件在classes目录下但xxx。class文件应该按照org。springframework。xx这样放置在jar中的根目录中
  所以classes和lib你也能意识到,这个设计是独特的。早期jar包内携带依赖是采用如mavenshadeplugin的做法,把依赖的class文件拷贝到目标jar中,但也会造成重名(全限定名)的类会出现覆盖的情况。后来SpringBoot为了避免覆盖的情况,修改了打包机制,放弃了mavenshadeplugin那种拷贝class的方式,调整为依赖原始jar包;这同时意味着改变了Jar标准的运行机制,那么要想让classes和lib中代码能够正常运行,你试想一下如果没有自定义的classLoader来加载这些类文件,可以嘛?四、自定义类加载器的运行机制
  自定义类加载器的常规处理:指定资源指定委托关系指定线程上下文类加载器调用逻辑入口方法4。1指定资源
  构造方法中基于jar包的文件系统信息,构造Archive对象publicExecutableArchiveLauncher(){this。archivecreateArchive();}protectedfinalArchivecreateArchive()throwsException{ProtectionDomainprotectionDomaingetClass()。getProtectionDomain();CodeSourcecodeSourceprotectionDomain。getCodeSource();URIlocation(codeSource!null)?codeSource。getLocation()。toURI():Stringpath(location!null)?location。getSchemeSpecificPart():if(pathnull){thrownewIllegalStateException(Unabletodeterminecodesourcearchive);}FilerootnewFile(path);if(!root。exists()){thrownewIllegalStateException(Unabletodeterminecodesourcearchivefromroot);}return(root。isDirectory()?newExplodedArchive(root):newJarFileArchive(root));}复制代码
  采集jar包中的classes和lib目录下的归档文件。后边创建ClassLoader的时候作为参数传入OverrideprotectedListgetClassPathArchives()throwsException{ListarchivesnewArrayList(this。archive。getNestedArchives(this::isNestedArchive));postProcessClassPathArchives(archives);}protectedbooleanisNestedArchive(Archive。Entryentry){if(entry。isDirectory()){returnentry。getName()。equals(BOOTINFCLASSES);}returnentry。getName()。startsWith(BOOTINFLIB);}复制代码publicstaticvoidmain(String〔〕args)throwsException{newJarLauncher()。launch(args);}复制代码4。2创建自定义ClassLoaderprotectedvoidlaunch(String〔〕args)throwsException{JarFile。registerUrlProtocolHandler();创建类加载器,并指定归档文件ClassLoaderclassLoadercreateClassLoader(getClassPathArchives());launch(args,getMainClass(),classLoader);}创建类加载器,将归档文件转换为URLprotectedClassLoadercreateClassLoader(Listarchives)throwsException{ListURLurlsnewArrayList(archives。size());for(Archivearchive:archives){urls。add(archive。getUrl());}returncreateClassLoader(urls。toArray(newURL〔0〕));}父加载器是AppClassLoaderprotectedClassLoadercreateClassLoader(URL〔〕urls)throwsException{getClass()。getClassLoader()是系统类加载器,因为默认情况下main方法所在类是由SystemClassLoader加载的,默认情况下是AppClassLoader。returnnewLaunchedURLClassLoader(urls,getClass()。getClassLoader());}复制代码4。3设置线程上下文类加载器,调用程序中的mainclasspublicstaticvoidmain(String〔〕args)throwsException{newJarLauncher()。launch(args);}protectedvoidlaunch(String〔〕args,StringmainClass,ClassLoaderclassLoader)throwsException{设置线程上下文类加载器Thread。currentThread()。setContextClassLoader(classLoader);调用MANIFEST。MF中配置的StartClass:xxx的main方法,还带入了参数createMainMethodRunner(mainClass,args,classLoader)。run();}复制代码
投诉 评论 转载

那条河(上)本文作者:范有生在村子前面,有一条河。一条没有名字的河,一条不知流淌了多少年的河。小时候问过我爸,咱们门前这条河叫什么名儿啊?我爸吐出长长的烟圈,悠悠地说,布……去海南过冬详,用纬度温度湿度风度说明哪里适合您海南省共有19个县市,其中有4个地级市:海口市、三亚市、儋州市、三沙市;5个县级市:文昌市、琼海市、万宁市、五指山市、东方市;10个县,其中4个县:澄迈县、临高县、屯昌县、定安……忍人所不忍,能人所不能一个人品有问题离他远一点,一个人心有问题他是改不了的,他会坏你的事,断你的运,好端端的生活都会把你叫得混乱不堪,和正能量的人命运认知都会提高,有些人必须要划清界限,近朱者赤近墨……IFFHS年度最佳球员候选梅西领衔,姆巴佩本泽马魔笛在列直播吧12月24日讯国际足球历史和统计联合会(IFFHS)公布了2022年年度最佳球员候选名单,共20人。名单如下,排序参考原文顺序。姆巴佩法国巴黎圣日耳曼格列兹曼……这种信用卡将被清理请在注销日前主动用卡2020年以来,各家银行纷纷发布公告,将对异常账户进行清理,一些长期没有交易并且没有资金的睡眠账户,以及同一客户名下的超量账户,都将被银行注销。2022年7月7日,银保监……勇士队三连胜战绩回归西部前十,卫冕冠军的底蕴不一般北京时间12月29日,NBA常规赛,勇士队主场对阵爵士队,缺少了库里的情况下,勇士队能够战胜对手呢?主场作战的勇士队果然神勇,本赛季勇士队的客场战绩并不好,但是,主场战绩却是非……6月份,重点关注3大最具潜力爆发板块(附个股)日前召开的国务院常务会议提出,放宽汽车限购,阶段性减征部分乘用车购置税600亿元。四部委综合司联合发布的《关于开展2022新能源汽车下乡活动的通知》提出,今年5月至12月,拟定……做提眉手术后13天,和大家说说提眉手术关注度较高的6个问题提眉手术是大家比较关心的眼周年轻化手术之一,提眉手术可以改善上眼睑的皮肤松弛,眼尾下垂,三角眼等,提眉手术尤其是对于原有的双眼皮或者单眼皮形态较好的求美者,术后呈现的效果要更好……没有不适合的卷发,只是你没选对网红卷适合各种年龄阶段的妹子,非常显气质,很多网红妹子对这款发型都完全没有抵抗力十分显气质的凌乱烫发造型,相信任何一位美眉都无法抗拒它的美,再搭配上稀疏的空气式刘海,更加……到底喝多少酒会引起酒精性肝病,你知道吗?酒精性肝病并不单指常提到的酒精肝,也就是酒精性脂肪肝,主要包括如下三种类型:1、酒精性脂肪肝2、酒精性肝炎3、肝硬化这三种类型逐步进展,最终甚至会导致肝……泰国允许孕期超12周但未满20周女性接受检查及建议后堕胎9月26日,泰国政府公报网公示了一份由卫生部签署下发的文件,该文件内容主要涉及允许孕期超过12周但未满20周的女性在接受专业检查及建议后选择人工流产相关规定,以预防、解决青少年……从原理和源码梳理SpringbootFatJar的机制一、概述SpringBootFatJar的设计,打破了标准jar的结构,在jar包内携带了其所依赖的jar包,通过jar中的main方法创建自己的类加载器,来识别加载运行……
肝气郁结,逍遥丸加味逍遥丸到底该怎么选?看看中医专家怎么说三星新机曝光,144Hz2亿像素,骁龙8Gen2处理器坐镇2022下半年,房价下跌30前,商业银行将会出现5个特殊情况8000字详解数字化转型转什么?如何转?汤加是个什么样的国家?热门美白精华怎么选?SKII小灯泡均匀肤色,资生堂刺激度低打台球的天赋,看看就知道了解宝宝精细动作的发展中国电信天翼一号2022手机正式开售,6128GB售价149太强了!挪威15金破纪录,中国8金再创新高,韩国退步,英国尴马苏是自创了一个色号吗?这肤色也太白了,穿得花里胡哨也很高贵走进石岛,领略赤山背后的人文历史捧不红的女星,出道5年担任15部剧主角,网友,这就是命有缺点造句用有缺点造句大全1991年山西电视台拍了部电视剧,无一著名演员,却成不朽经典宇航局造句用宇航局造句大全为什么生意越来越难做了?小灰兔受伤向村民求助获救,还吃上了青菜胡萝卜灰枣怎么清洗灰枣怎么洗干净款九个月宝宝食谱182。7亿公里外,旅行者2号传回的数据,科学家看了感到很失子午觉可对抗亚健康江阴是哪里(江阴的经济到底多强)关于视频号,给你说点掏心窝的大实话

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找七猫云易事利