我的公众号:IT周瑜
前几天有一个粉丝朋友问我:SpringBoot中的LaunchedURLClassLoader的作用是什么?
今天抽时间给大家来分析分析,当我们在SpringBoot应用中引入spring-boot-maven-plugin
后,执行mvn clean package
命令,即可将SpringBoot应用打成一个可直接java -jar
执行的Jar包,解压该Jar包看看里面的内容: BOOT-INF目录下主要包括classes和lib目录:
classes目录包括应用本身的字节码和配置文件: 而lib目录则包括了应用中第三方依赖
我们再来看MANIFEST.MF文件: 该文件的内容为:
- 其中Spring-Boot-Classes和Spring-Boot-Lib对应的就是上面介绍的BOOT-INF目录下的classes和lib目录,表示当前应用用到的所有类
- Start-Class:我们定义的应用启动类,com.zhouyu.MyApplication
- Main-Class:真正的应用启动类,org.springframework.boot.loader.JarLauncher
基于Java基础知识,当我们运行java -jar hello-spring-boot.jar
时,java命令会找Jar包中的MANIFEST.MF文件中指定的Main-Class,并执行它的main()方法,所以大家注意,最开始运行的是JarLauncher中的main()方法,而不是MyApplication中的main()方法,而JarLauncher类就在:
那为什么要多此一举呢?为什么Main-Class不直接为MyApplication呢?我们先看看org.springframework.boot.loader.JarLauncher
中main()方法的实现: 在launch()方法中主要分为三步:
- 第一步,创建一个ClassLoader,具体类型为LaunchedURLClassLoader
- 第二步,找到真正的启动类,最后的getMainClass()取的就是Start-Class的值,也就是MyApplication类
- 第三步,会把上面创建的ClassLoader设置为线程上下文类加载器,并运行MyApplication中的main()方法,真正开始启动SpringBoot
也就是说,在执行java -jar hello-spring-boot.jar
时,会先创建一个自定义类加载器LaunchedURLClassLoader,并且会成为线程上下文类加载器,然后再运行MyApplication的main()方法,也就是说在运行MyApplication的main()方法时遇到需要加载类时就会用LaunchedURLClassLoader来加载。
那为什么SpringBoot要自定义一个LaunchedURLClassLoader类加载器呢?
因为普通的类加载器只能加载Jar包中直接的类,而不能加载Jar包中的Jar包中的类 ,比如一个普通的Jar包内容为: 普通的类加载器能加载到MyAppliation和ZhouyuController两个类,但是如果这个Jar包中还有一个third.jar包,那么普通的类加载器则没办法加载third.jar中的类了,而SpringBoot想要的效果就是将应用的所有第三方依赖jar全部放在应用的Jar包中,也就是Jar包中包含Jar包,所以需要在启动的最开始自定义一个类加载器,后续就由这个自定义类加载器来加载BOOT-INF目录下的classes目录和lib目录下的类了。
比如hello-spring-boot.jar中就有一个JarFileArchive类,它里面就有一个unpack()方法,自然就是用来解压第三方Jar包并加载其中的类的:
我们也会把SpringBoot插件打出来的Jar叫做FatJar,因为它包含了很多第三方依赖,所以很胖,SpringBoot就像大家的父母,小时候上学,父母会在早上把你一天要用到的东西都准备好放在书包(FatJar)里,你需要时直接从书包里取就可以了,而不用额外再去家里或商店去取了。
我是大都督周瑜,欢迎关注我的公众号:IT周瑜,没有废话,全程干货。