SpringBoot应用启动原理(二) 扩展URLClassLoader实现嵌套jar加载

在上篇文章《SpringBoot应用启动原理(一) 将启动脚本嵌入jar》中介绍了SpringBoot如何将启动脚本与Runnable Jar整合为Executable Jar的原理,使得生成的jar/war文件可以直接启动
本篇将介绍SpringBoot如何扩展URLClassLoader实现嵌套jar的类(资源)加载,以启动我们的应用。

首先,从一个简单的示例开始

build.gradle

WebApp.java

执行 gradle build 构建jar包,里面包含 应用程序 第三方依赖 以及SpringBoot 启动程序 ,其目录结构如下

查看MANIFEST.MF的内容(MANIFEST.MF文件的作用请自行GOOGLE)

可以看到,jar的启动类为 org.springframework.boot.loader.JarLauncher ,而并不是我们的 com.manerfan.SpringBoot.theory.WebApp ,应用程序入口类被标记为了Start-Class

jar启动并不是通过应用程序入口类,而是通过JarLauncher代理启动。其实SpringBoot拥有3中不同的Launcher: JarLauncher 、 WarLauncher 、 PropertiesLauncher

SpringBoot使用Launcher代理启动,其最重要的一点便是可以自定义ClassLoader,以实现对jar文件内(jar in jar)或其他路径下jar、class或资源文件的加载
关于ClassLoader的更多介绍可参考 《深入理解JVM之ClassLoader》

SpringBoot抽象了Archive的概念,一个Archive可以是jar(JarFileArchive),可以是一个文件目录(ExplodedArchive),可以抽象为统一访问资源的逻辑层。

上例中,spring-boot-theory-1.0.0.jar既为一个JarFileArchive,spring-boot-theory-1.0.0.jar!/BOOT-INF/lib下的每一个jar包也是一个JarFileArchive
将spring-boot-theory-1.0.0.jar解压到目录spring-boot-theory-1.0.0,则目录spring-boot-theory-1.0.0为一个ExplodedArchive

按照定义,JarLauncher可以加载内部 /BOOT-INF/lib 下的jar及 /BOOT-INF/classes 下的应用class

其实JarLauncher实现很简单

其主入口新建了JarLauncher并调用父类Launcher中的launch方法启动程序
再创建JarLauncher时,父类ExecutableArchiveLauncher找到自己所在的jar,并创建archive

在Launcher的launch方法中,通过以上archive的getNestedArchives方法找到/BOOT-INF/lib下所有jar及/BOOT-INF/classes目录所对应的archive,通过这些archives的url生成LaunchedURLClassLoader,并将其设置为线程上下文类加载器,启动应用

至此,才执行我们应用程序主入口类的main方法,所有应用程序类文件均可通过/BOOT-INF/classes加载,所有依赖的第三方jar均可通过/BOOT-INF/lib加载

在分析LaunchedURLClassLoader前,首先了解一下URLStreamHandler

java中定义了URL的概念,并实现多种URL协议(见 URL ) * http* * file* * ftp* * jar* 等,结合对应的URLConnection可以灵活地获取各种协议下的资源

对于jar,每个jar都会对应一个url,如
jar:file:/data/spring-boot-theory/BOOT-INF/lib/spring-aop-5.0.4.RELEASE.jar!/

jar中的资源,也会对应一个url,并以'!/'分割,如
jar:file:/data/spring-boot-theory/BOOT-INF/lib/spring-aop-5.0.4.RELEASE.jar!/org/springframework/aop/SpringProxy.class

对于原始的JarFile URL,只支持一个'!/',SpringBoot扩展了此协议,使其支持多个'!/',以实现jar in jar的资源,如
jar:file:/data/spring-boot-theory.jar!/BOOT-INF/lib/spring-aop-5.0.4.RELEASE.jar!/org/springframework/aop/SpringProxy.class

自定义URL的类格式为[pkgs].[protocol].Handler,在运行Launcher的launch方法时调用了 JarFile.registerUrlProtocolHandler() 以注册自定义的 Handler

在处理如下URL时,会循环处理'!/'分隔符,从最上层出发,先构造spring-boot-theory.jar的JarFile,再构造spring-aop-5.0.4.RELEASE.jar的JarFile,最后构造指向SpringProxy.class的
JarURLConnection ,通过JarURLConnection的getInputStream方法获取SpringProxy.class内容

从一个URL,到读取其中的内容,整个过程为

URLClassLoader可以通过原始的jar协议,加载jar中从class文件
LaunchedURLClassLoader 通过扩展的jar协议,以实现jar in jar这种情况下的class文件加载

构建war包很简单

构建出的war包,其目录机构为

MANIFEST.MF内容为

此时,启动类变为了 org.springframework.boot.loader.WarLauncher ,查看WarLauncher实现,其实与JarLauncher并无太大差别

差别仅在于,JarLauncher在构建LauncherURLClassLoader时,会搜索BOOT-INF/classes目录及BOOT-INF/lib目录下jar,WarLauncher在构建LauncherURLClassLoader时,则会搜索WEB-INFO/classes目录及WEB-INFO/lib和WEB-INFO/lib-provided两个目录下的jar

如此依赖,构建出的war便支持两种启动方式

PropretiesLauncher 的实现与 JarLauncher WarLauncher 的实现极为相似,通过PropretiesLauncher可以实现更为轻量的thin jar,其实现方式可自行查阅源码



  • springboot鍚姩娴佺▼鏄粈涔?
    绛旓細1. 鍒濆鍖Spring搴旂敤涓婁笅鏂囷細杩欐槸Spring妗嗘灦鐨勬牳蹇冮儴鍒嗭紝Springboot搴旂敤鍚姩鏃堕鍏堜細鍒涘缓骞跺垵濮嬪寲涓涓狝pplicationContext瀹炰緥銆傚湪杩欎釜杩囩▼涓紝浼氳鍙栧苟瑙f瀽閰嶇疆鏂囦欢锛屼负鍚庣画鐨凚ean娉ㄥ唽鍜屼緷璧栨敞鍏ュ仛鍑嗗銆2. 鍔犺浇閰嶇疆锛歋pringboot浼氭牴鎹」鐩腑鐨勯厤缃俊鎭潵閰嶇疆搴旂敤鐨勫悇椤瑰弬鏁般傝繖浜涢厤缃寘鎷簲鐢ㄧ殑鍩烘湰灞炴с佹暟鎹簱...
  • springboot鍚姩娴佺▼
    绛旓細springboot鍚姩娴佺▼濡備笅锛氬惎鍔ㄦ祦绋嬩富瑕佸垎涓轰笁涓儴鍒嗭紝绗竴閮ㄥ垎杩涜銆丼pringApplication鐨勫垵濮嬪寲妯″潡锛岄厤缃竴浜涘熀鏈殑鐜鍙橀噺銆佽祫婧愩佹瀯閫犲櫒銆佺洃鍚櫒锛岀浜岄儴鍒嗗疄鐜颁簡搴旂敤鍏蜂綋鐨勫惎鍔ㄦ柟妗堬紝鍖呮嫭鍚姩娴佺▼鐨勭洃鍚ā鍧椼佸姞杞介厤缃幆澧冩ā鍧椼傚強鏍稿績鐨勫垱寤轰笂涓嬫枃鐜妯″潡锛岀涓夐儴鍒嗘槸鑷姩鍖栭厤缃ā鍧楋紝璇ユā鍧椾綔涓簊pringboot鑷...
  • SpringBoot鍚姩鍘熺悊鍒嗘瀽
    绛旓細棣栧厛閬嶅巻鎵ц鎵鏈夐氳繃SpringFactoriesLoader锛屽湪褰撳墠classpath涓嬬殑META-INF/spring.factories涓煡鎵炬墍鏈夊彲鐢ㄧ殑SpringApplicationRunListeners骞跺疄渚嬪寲銆傝皟鐢ㄥ畠浠殑starting()鏂规硶锛岄氱煡杩欎簺鐩戝惉鍣SpringBoot搴旂敤鍚姩銆傚垱寤哄苟閰嶇疆褰撳墠SpringBoot搴旂敤灏嗚浣跨敤鐨凟nvironment锛屽寘鎷綋鍓嶆湁鏁堢殑PropertySource浠ュ強Profile銆傞亶鍘嗚皟鐢ㄦ墍鏈...
  • springboot鍚姩娴佺▼
    绛旓細1銆佹牴鎹甤lasspath涓嬫槸鍚﹀瓨鍦(ConfigurableWebApplicationContext)鍒ゆ柇鏄惁瑕鍚姩涓涓獁eb applicationContext銆2銆SpringFactoriesInstances鍔犺浇classpath涓嬫墍鏈夊彲鐢ㄧ殑ApplicationContextInitializer 3銆丼pringFactoriesInstances鍔犺浇classpath涓嬫墍鏈夊彲鐢ㄧ殑ApplicationListener ...
  • springboot鍚姩杩囩▼鏄?
    绛旓細SpringBoot鐨鍚姩涓昏鏄氳繃瀹炰緥鍖朣pringApplication鏉ュ惎鍔ㄧ殑銆傚湪浜嗚ВSpringBoot鐨勫惎鍔ㄦ祦绋嬬殑鏃跺欙紝鎴戜滑鍏堢湅涓涓嬩竴涓SpringBoot搴旂敤鏄浣曞惎鍔ㄧ殑锛屽涓嬫槸涓涓畝鍗曠殑SpringBoot绋嬪簭锛岄潪甯哥殑绠娲侊紝浠栨槸濡備綍鍋氬埌鐨勫憿锛屾垜浠帴涓嬫潵灏卞皢涓姝ユ鍒嗚В銆係pringBoot鍦ㄥ惎鍔ㄦ椂锛岄氳繃ConfigurationClassPostProcessor.postProcess...
  • springboot鍚姩杩囩▼鏄?
    绛旓細鍚姩锛氭瘡涓SpringBoot绋嬪簭閮芥湁涓涓富鍏ュ彛锛屼篃灏辨槸main鏂规硶锛宮ain閲岄潰璋冪敤SpringApplication.run()鍚姩鏁翠釜spring-boot绋嬪簭锛岃鏂规硶鎵鍦ㄧ被闇瑕佷娇鐢ˊSpringBootApplication娉ㄨВ銆備互鍙夽ImportResource娉ㄨВ(if need)锛孈SpringBootApplication鍖呮嫭涓変釜娉ㄨВ锛屽姛鑳藉涓嬶細@EnableAutoConfiguration锛歋pringBoot鏍规嵁搴旂敤鎵澹版槑鐨...
  • springboot鍚姩娴佺▼鏄粈涔?
    绛旓細SpringBoot鐨勫惎鍔ㄤ富瑕佹槸閫氳繃瀹炰緥鍖朣pringApplication鏉ュ惎鍔ㄧ殑銆傚惎鍔ㄨ繃绋嬩富瑕佸仛浜嗕互涓嬪嚑浠朵簨鎯咃細閰嶇疆灞炴с佽幏鍙栫洃鍚櫒锛屽彂甯冨簲鐢ㄥ紑濮嬪惎鍔ㄤ簨浠跺垵銆佸鍖栬緭鍏ュ弬鏁般侀厤缃幆澧冿紝杈撳嚭banner銆佸垱寤轰笂涓嬫枃銆侀澶勭悊涓婁笅鏂囥佸埛鏂颁笂涓嬫枃(鍔犺浇tomcat瀹瑰櫒)銆佸啀鍒锋柊涓婁笅鏂囥佸彂甯冨簲鐢ㄥ凡缁忓惎鍔ㄤ簨浠躲佸彂甯搴旂敤鍚姩瀹屾垚浜嬩欢銆傚湪SpringBoot...
  • SpringBoot鐨鍚姩杩囩▼鍙婇儴鍒嗘敞瑙
    绛旓細涓涓畝鍗曠殑 SpringBoot 搴旂敤鍙渶瑕佷笁姝ワ細 1.鍦 pom.xml 涓紩鍏ユ墍闇瑕佺殑渚濊禆 2.鍦 application.yml 閰嶇疆鎵闇鐨勬暟鎹簮 3.鍦鍚姩绫讳腑鍔犲叆 @SpringBootApplication 娉ㄨВ 浠ュ強 run 鏂规硶 鍚姩娴佺▼ 1.SpringApplication.run()鍚姩 2.鏂板缓 SpringApplication 瀹炰緥锛屼富瑕佹槸鍒濆鍖栦竴浜涙垚鍛樺彉閲忥紝鍙傛暟...
  • 杩愯springboot鏈夊摢鍑犵鏂规硶
    绛旓細1銆佸湪IDE涓繍琛 鍦‥clipse銆両DEA涓洿鎺ヨ繍琛岋紝鍙堟湁浠ヤ笅涓ょ鏂瑰紡銆俲ar鍖呮柟寮忥細Spring Boot榛樿閲囩敤jar鍖呭唴宓孴omcat銆丣etty绛塖erver鐨勬柟寮忥紝骞堕渶瑕佹彁渚涗竴涓惈鏈塵ain鏂规硶鐨勪富绫汇傝繖涓椂鍊欙紝鐩存帴鍦↖DE涓繍琛岃繖涓猰ain鏂规硶灏辫兘鍚姩Spring Boot搴旂敤浜嗐倃ar鍖呮柟寮忥細濡傛灉浣犵殑搴旂敤鏀硅鎴愪簡war鍖呮柟寮忛儴缃诧紝杩欎釜鏃跺欏氨闇瑕佸湪...
  • springboot鍚姩杩愯鐗瑰畾浠g爜
    绛旓細涓銆佽儗鏅笌闇姹傝鏄 鍦⊿pring Boot搴旂敤涓紝鏈夋椂鎴戜滑闇瑕佸湪搴旂敤鍚姩鏃惰繍琛屼竴浜涚壒瀹氱殑浠g爜锛屾瘮濡傝繘琛屾暟鎹垵濮嬪寲銆侀鍔犺浇缂撳瓨绛夋搷浣溿備负浜嗘弧瓒宠繖绉嶉渶姹傦紝Spring Boot鎻愪緵浜嗕袱涓帴鍙o細ApplicationRunner鍜孋ommandLineRunner銆傝繖涓や釜鎺ュ彛閮芥彁渚涗簡涓涓猺un鏂规硶锛Spring Boot搴旂敤鍚姩鍚庝細鎵ц杩欎袱涓帴鍙d腑鐨剅un鏂规硶銆備簩銆...
  • 扩展阅读:java spring boot ... spring-boot-plugin ... spring boot启动5个步骤 ... springboot实战视频 ... spring boot 完整教程 ... spring cloud ... springboot netty ... springboot启动流程 ... spring mvc ...

    本站交流只代表网友个人观点,与本站立场无关
    欢迎反馈与建议,请联系电邮
    2024© 车视网