0
votes

When deploying a Springboot maven project(version 2.3.4.RELEASE) to an external Tomcat container,official guide says you need to mark the "spring-boot-starter-tomcat" dependency as provided ,but actually even if without doing that,the final war package which contains lib like "spring-boot-starter-tomcat","tomcat-embed-core" and "tomcat-embed-websocket" also works fine in tomcat8.5.54 or tomcat 9.0 ,so I am confused about that,"Do we really need set spring-boot-starter-tomcat as provided or not?" ,anyone could explains why? "Traditional Deployment"

1
I am not sure I understood the question but I will try. In 'Traditional deployment', you create a WAR file and deploy it in a servlet container (such as Tomcat, Jetty, etc). In that case, you setup the tomcat dependencies as provided since all the dependency libraries will be provided to the application by the servlet containter. So, they don't need to be bundled with the WAR file (to reduce the file size, or something) - If you remove the provided setting in the dependencies, then the needed, minimum Tomcat libraries will be included in your WAR file (since it Spring Boot will need them) - blurfus
@blurfus: Spring Boot adds also the provided dependencies to the WAR file (so it can be run with java -jar), but it places them in a place where Tomcat's classloader does not look: /WEB-INF/lib-provided. - Piotr P. Karwasz
@PiotrP.Karwasz is that true even if packing the app as a WAR? - blurfus
Yes, the Spring Boot Maven plugin does it: "provided dependencies are placed in WEB-INF/lib-provided..." - Piotr P. Karwasz

1 Answers

0
votes

You don't want to have multiple versions of the same classes on the classpath. This can lead to many errors during runtime. For example, if you have a

public class MyServlet implements javax.servlet.Servlet

and you package both MyServlet.class and javax.servlet-api.jar (which contains javax.servlet.Servlet) into your application, you might get an error:

Servlet class MyServlet is not a javax.servlet.Servlet

What happens is: when the application classloader loads MyServlet, it looks for javax.servlet.Servlet in the application first and it finds it in javax.servlet-api.jar. The server compares this class with the one loaded by the server's classloader and concludes that they differ, since classes from different JARs are not equal. If the application is shipped without javax.servlet-api.jar this does not happen: the classloader does not find javax.servlet.Servlet in its own classpath, so it looks in the parent classloader.

Remark: This example can not actually be reproduced on Tomcat. Due probably to many incorrectly packaged applications the WebappClassLoader has an exception to the class loading rule: special classes such as those starting with javax.servlet or org.apache.tomcat are always loaded from the server's classloader (cf. the source code for a list of these exceptions).

TL;DR: due to the remark above, leaving spring-boot-starter-tomcat in the compile scope probably will do no harm, but it is a risk not worth taking.