Preface
For an analysis of the startup principle of spring boot itself, please refer to: http://www.VeVB.COM/article/141478.htm
The ClassLoader inheritance relationship in Spring boot can run the demo provided below, and run it in different scenarios. You can know the ClassLoader inheritance relationship of Spring boot applications in different scenarios.
https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-classloader-context
There are three situations:
In the IDE, the run main function directly runs the ClassLoader of Spring is directly SystemClassLoader. ClassLoader's urls contain all jars and their own target/classes
========= Spring Boot Application ClassLoader Urls ===============
ClassLoader urls: sun.misc.Launcher$AppClassLoader@2a139a55
file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/classes/
file:/Users/hengyunabc/.m2/repository/org/springframework/cloud/spring-cloud-starter/1.1.9.RELEASE/spring-cloud-starter-1.1.9.RELEASE.jar
file:/Users/hengyunabc/.m2/repository/org/springframework/boot/spring-boot-starter/1.4.7.RELEASE/spring-boot-starter-1.4.7.RELEASE.jar
...
Run as fat jar
mvn clean packagejava -jar target/demo-classloader-context-0.0.1-SNAPSHOT.jar
The ClassLoader that executes the application's main function is LaunchedURLClassLoader, and its parent is SystemClassLoader.
========== ClassLoader Tree===============
org.springframework.boot.loader.LaunchedURLClassLoader@1218025c
- sun.misc.Launcher$AppClassLoader@6bc7c054
-- sun.misc.Launcher$ExtClassLoader@85ede7b
And LaunchedURLClassLoader的urls are BOOT-INF/classes!/ directory in fat jar and all jars in the BOOT-INF/lib.
========= Spring Boot Application ClassLoader Urls ===============
ClassLoader urls: org.springframework.boot.loader.LaunchedURLClassLoader@1218025c
jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/spring-boot-1.4.7.RELEASE.jar!/
jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/spring-web-4.3.9.RELEASE.jar!/
...
The urls of SystemClassLoader are demo-classloader-context-0.0.1-SNAPSHOT.jar itself.
========== System ClassLoader Urls ===============
ClassLoader urls: sun.misc.Launcher$AppClassLoader@6bc7c054
file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-SNAPSHOT.jar
Run as the decompressed directory
mvn clean packagecd targetunzip demo-classloader-context-0.0.1-SNAPSHOT.jar -d democd demojava org.springframework.boot.loader.PropertiesLauncher
The ClassLoader that executes the application's main function is LaunchedURLClassLoader , and its parent is SystemClassLoader .
========== ClassLoader Tree===============
org.springframework.boot.loader.LaunchedURLClassLoader@4aa298b7
- sun.misc.Launcher$AppClassLoader@2a139a55
-- sun.misc.Launcher$ExtClassLoader@1b6d3586
The urls of LaunchedURLClassLoader are the jar packages below BOOT-INF/classes/ and /BOOT-INF/lib/ in the decompression directory.
========= Spring Boot Application ClassLoader Urls ===============
ClassLoader urls: org.springframework.boot.loader.LaunchedURLClassLoader@4aa298b7
file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/BOOT-INF/classes/
jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/BOOT-INF/lib/bcpkix-jdk15on-1.55.jar!/
jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/BOOT-INF/lib/bcprov-jdk15on-1.55.jar!/
jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/BOOT-INF/lib/classmate-1.3.3.jar!/
The urls of SystemClassLoader only have the current directory:
========== System ClassLoader Urls ===============
ClassLoader urls: sun.misc.Launcher$AppClassLoader@2a139a55
file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/
In fact, there are two other ways to run: mvn spring-boot:run and mvn spring-boot:run -Dfork=true , but it is rarely used and will not be discussed separately. If you feel interested, you can run down by yourself.
Summary of the inheritance relationship of ClassLoader in spring boot
When the main function is executed in the IDE, there is only one ClassLoader, that is, SystemClassLoader
When running as fat jar, there is a LaunchedURLClassLoader whose parent is SystemClassLoader
The urls of LaunchedURLClassLoader are the jars under BOOT-INF/classes and BOOT-INF/lib in fat jars. The urls of SystemClassLoader are fat jars themselves.
When running the unzipped directory, it is similar to fat jar, but the url is in the form of a directory. The directory format will have better compatibility.
Spring boot versions 1.3. and 1.4.
In spring boot 1.3.* version
The spring boot loader class is placed in the fat jar
The packaging structure changes of spring boot 1.4 were introduced by this commit
https://github.com/spring-projects/spring-boot/commit/87fe0b2adeef85c842c009bfeebac1c84af8a5d7
The original intention of this commit is to simplify the inheritance relationship of the classloader, implement the LaunchedURLClassLoader in an intuitive parent-first way, and at the same time, the packaging structure is closer to the traditional war package application.
However, this change caused many complex problems. From the above we analyzed the ClassLoader inheritance relationship a little dizzy.
Some impacts of the current ClassLoader inheritance relationship
There are many users who may find that some code runs well in the IDE but does not work when actually deployed. Many times it is caused by the structure of ClassLoader. Here are some cases.
demo.jar!/BOOT-INF/classes!/ This way the url does not work
Because spring boot extends the standard jar protocol, allowing it to support multi-layer jar in jar and directory in jar. Refer to the analysis of spring boot application startup principle
Although there will be jar in jar in spring boot 1.3, some relatively robust code can handle this situation, such as tomcat8 itself supports jar in jar.
However, most of the code will not support multiple URLs such as demo.jar!/BOOT-INF/classes!/ , so in spring boot1.4, many library codes will be invalid.
Resource issues under demo.jar!/META-INF/resources
In the servlet 3.0 specification, applications can place static resources under META-INF/resources, and the servlet container will support reading. But from the above inheritance results, we can find a problem:
This creates some strange phenomena:
In addition, the official JSSP example of spring boot only supports the packaging format of war and does not support fat jar, which is also caused by this.
Issues with the return value of getResource("") and getResources("")
The semantics of getResource("") is to return the first url of the ClassLoader urls. Many times users think that this is their own class directory, or the jar url.
But in fact, because ClassLoader loads the urls list, it is random, which is related to the low-level implementation of OS, and it cannot guarantee that the order of urls is the same. Therefore, the result returned by getResource("") is often different.
However, many libraries or applications rely on this code to locate scanning resources, so that they will not work under spring boot.
In addition, it is worth noting that spring boot runs in three different forms, and the results returned by getResources("") are also different. Users can change the code in the demo by themselves and print the results.
In short, don't rely on these two APIs, it's best to place a resource to locate it yourself. Or directly use the resource scanning mechanism provided by spring itself.
Wild classification problem similar to classpath*:**-service.xml
The user has multiple code modules, and multiple *-service.xml spring configuration files are placed under different modules.
If a user uses wildcards like classpath*:**-service.xml to load resources, it is very likely that it can load correctly when running in the IDE, but it cannot load under the fat jar.
You can see the relevant analysis from spring's own documentation:
https://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/core/io/support/PathMatchingResourcePatternResolver.html
WARNING: Note that “classpath:” when combined with Ant-style patterns will only work reliably with at least one root directory before the pattern starts, unless the actual target files reside in the file system. This means that a pattern like “classpath:*.xml” will not retrieve files from the root of jar files but rather only from the root of expanded directories. This originates from a limitation in the JDK's ClassLoader.getResources() method which only returns file system locations for a passed-in empty String (indicating potential roots to search). This ResourcePatternResolver implementation is trying to mitigate the jar root lookup limitation through URLClassLoader introduction and "java.class.path" manifest evaluation; however, without portability guarantees.
That is to say, when using classpath* to match other jar packages, there needs to be a layer of directory in front, otherwise it will not match. This is caused by the ClassLoader.getResources() function.
Because when running in the IDE, the other modules that the application depends on are usually a classes directory, so there is usually no problem.
However, when running with fat jar, other modules are packaged as a jar and placed under BOOT-INF/lib, so the wild-distribution will fail at this time.
Summarize
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.