Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why did Spring Boot 1.4 change its jar layout to locate application classes under BOOT-INF?

Tags:

spring-boot

(This is primarily a history question. Pivotal recommended that all forum discussions take place on StackOverflow, which is why I am asking it here.)

What are the reasons that the Spring Boot project used to justify moving an application's classes and dependencies from the "top level" of the executable jar format to be underneath BOOT-INF/?

Just trying to guess, it seems that this makes it easy to extract only the application-related classes and jars from the fat jar with a simple java -xf the-jar.jar BOOT-INF/classes command. Was that it? Was there some other reason?

like image 332
Laird Nelson Avatar asked Oct 27 '16 19:10

Laird Nelson


People also ask

What is boot INF jar?

BOOT-INF : Spring Boot applications load from the BOOT-INF folder. Therefore the application classes must be placed in a nested BOOT-INF/classes directory. Dependencies should be placed in a nested BOOT-INF/lib directory.

What is a classpath in spring boot?

It's a path inside your project where you place resources. During the build step, Maven will take files in there and place them in the appropriate place for you to use them in your runtime classpath, eg in an executable . jar , some physical file system location used in the classpath (with java 's -cp option), etc.

How do I know if a jar is classpath?

A pragmatic way: Class. forName("com. myclass") where com. myclass is a class that is inside (and only inside) your target jar; if that throws a ClassNotFoundException , then the jar is not on you current classpath.

What is the starter that can be used to add spring boot dependency jars?

Adding Classpath Dependencies. Spring Boot provides a number of “Starters” that let you add jars to your classpath. Our applications for smoke tests use the spring-boot-starter-parent in the parent section of the POM. The spring-boot-starter-parent is a special starter that provides useful Maven defaults.


1 Answers

TL;DR

Packaging application classes in the root of the jar required Spring Boot's class loader to use an unconventional delegation model and also caused problems with Java agents.

Detailed explanation

When a jar file is launched with java -jar all of the classes in the root of the jar are on the class path of the system class loader. In a Spring Boot fat jar, this includes the classes for the launcher which is responsible for creating a class loader that can load the application's classes and their dependencies that are nested inside the fat jar.

In Spring Boot 1.3 and earlier, application classes are packaged in the root of a fat jar file. This means that they are on the class path of the system class loader. With a standard, parent first delegation model this would mean that application classes would be loaded by the system class loader rather than Spring Boot's class loader. This is problematic as it's only Spring Boot's class loader that can load the classes from the dependencies that are nested inside the fat jar. The result being that the application cannot load the classes of any of its dependencies.

Spring Boot 1.3 overcame this problem by using an unconventional delegation model for its class loader. It created a new class loader using the URLs from the system class loader but not using the system class loader as a parent – the system class loader's parent was used instead. This meant that Spring Boot's class loader would be used to load the application's classes in the root of the jar and the classes of the application's dependencies in the nested jars.

This approach had some drawbacks. The first was that it made Spring Boot's class loader rather complex. The second was that it broke a number of assumptions that Java agents make with regards to how their classes will be loaded. We worked around a couple of these but it became clear that we were fighting a losing battle.

Spring Boot 1.4 rearranges a fat jar to place application classes in BOOT-INF/classes (it also moves nested jars to BOOT-INF/lib but that has no effect from a class loading perspective). Moving the application classes into BOOT-INF/classes means that they are no longer on the class path of the system class loader. This means that Spring Boot's class loader can be configured to load classes from BOOT-INF/classes and from within the jars in BOOT-INF/lib and use the system class loader as its parent. Java agents can be packaged in the root of the jar from where they'll be loaded by the system class loader as usual.

For further reading you may be interested in the message on the commit that introduced the change and the other issues to which it links.

like image 50
Andy Wilkinson Avatar answered Oct 13 '22 00:10

Andy Wilkinson