Reduce The Size of Springboot Jar - spring

I would like to reduce the footprint of a fat springboot jar file.
The answer to this question actually covers everything Developing spring boot application with lower footprint
We need to include only the dependencies that we need and do not use auto configuration
My question is:
1- is there anyway (e.g. a script) to list the only used dependencies in a springboot project. i am actually doing trial and error to see if i need a dependency or not.
2- is there anyway to list the AutoConfiguration classes that i have to exclude, i can go and debug to see what springboot is auto configuring and pickup what i dont need, however i am looking for something like a script to check the code and give me a list of the AutoConfiguration classes that i have to exclude.
Gradle is used for dependency management.

If you are using Gradle you can see the full dependency list in a very good and interactive way via command gradle --scan then you can exclude some of the repeated ones.

Related

Spring Boot 3 Build vs Runtime Initialization Hints

I am working on creating an internal library/starter for my team that will add support for creating native images, providing all of the hints that our currently unsupported dependencies will need. I really like providing the metadata via code (using RuntimeHintsRegistrar), but there are also certain classes that need to be initialized at build time for whatever reason.
Right now I'm passing the --initialize-at-build-time and the classes to the Spring Boot Maven Plugin via the BP_NATIVE_IMAGE_BUILD_ARGUMENTS, but ideally I'd like to avoid each consuming app having to include this in their own POM's plugin configuration.
I also understand that I can go more low-level and provide the argument inside of the META-INF/native-image directory in a native-image.properties file, but I'm not sure whether that will play nice with the Spring-provided RuntimeHintsRegistrar effectively creating that underneath the covers.
What is the best way to tell native-image the classes that should be initialized at build time without each consuming app having to pass it in their own POM? Also, if I use the GraalVM tracing agent to generate hints, will those hints play nicely with the ones that RuntimeHintsRegistrar generates?
Thanks in advance!

How can I tell Spring Boot to place some of the classes at the root of the jar instead of BOOT-INF?

I'm trying to set a custom log Handler in my Spring Boot (version 2.6.3) application. The result is a ClassNotFound as described in this other question
Can't override java.util.logging.LogManager in a Spring Boot web application: Getting java.lang.ClassNotFoundException on already loaded class
Based on the answer to that question, it seems I need my Handler and all its dependencies to be placed into the root of the executable jar.
Is there a direct way to accomplish this during the Maven build, i.e. not by extracting and repackaging the jar myself post-build?
This issue is a result of BOOT-INF fat jar structure introduced by Spring Boot 1.4.
There is currently no straightforward solution, and it appears some of the Spring Boot maintainers do not agree there is a problem, so it could be a long time before the situation changes:
Issue #6626: Make it easier to package certain content in the root of a fat jar
Issue #12659: Starting executable war with -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager produces a ClassNotFoundException
WORKAROUND #1
I had to do two things to get my application working again with a custom log handler. 1) use Maven Shade to package up the log handler with all its dependencies, and 2) launch the app with using the PropertiesLauncher class in the command line instead of using java -jar:
java -cp executable.jar:logger-shaded.jar -Dloader.main=mypackage.myapp org.springframework.boot.loader.PropertiesLauncher
The executable.jar, logger-shaded.jar, and mypackage.myapp are placeholders specific to my project, so adjust accordingly.
WORKAROUND #2
If the handler is loaded from code in a config class or from main() instead of being specified in the file loaded via java.util.logging.config.file, as discussed in the comments to the answer in this other question, then everything works as expected. I actually prefer this over Workaround #1 as it results in a smaller deployment, but it does require writing a few more lines of code.

AspectJ dependency missing in spring boot 2.1.1

I was trying to create a new Spring Boot project using start.spring.io. Searching for dependencies, I found that there was no AspectJ starter available. Has this dependency removed/deprecated from Spring Boot starters? Here is a screen shot:
I, however, was able to find the dependency on maven repositories website:
It was removed indeed. #jwenting explained in a nutshell why. This starer is required if you want to create your own aspect or if you want to use some advanced AOP mode.
Most users don't need it and whenever a library requires it, its starter brings it automatically. Having a dedicated entry was confusing as we saw a very large amount of users picking this up for no good reason.
Also, please keep in mind that start.spring.io is not an exhaustive list of what you can do with Spring. We're focusing on the getting started experience only and avoiding cases that could lead to confusion. This one is a good example of the latter.
it's an implicit dependency, meaning you don't have to include it because it's automatically pulled in by anything that needs it.
You can still add it explicitly, but there's no need to (and afaik it's never been needed).

How can I include a resource file (logback.xml) in a project when building JAR but not when embedded in a WAR?

I'm working on a project that uses two maven projects (named core and webapp); core is built with JAR packaging and used for two different purposes: as a stand-alone app (essentially an executable JAR), and also embedded into webapp.
For its purpose as a stand-alone app, core needs to have its own logback configuration (a logback.xml file) that needs to be included on the classpath. Normal Maven convention would have me put it in src/main/resources/logback.xml. That works fine, but causes a problem when the core JAR is included in webapp. webapp needs to have its own logback configuration, but the container (tc Server or Jetty) is picking up the one from core.jar first.
I realize that logback can be told about a custom config location via a system property (-D on the command line) but that's not viable in a app container like Tomcat or Jetty.
I've read some other people asking about this situation, but none of the solutions I've seen sits well with me. One solution involved setting up a context listener that runs early in the webapp initialization and explicitly configures logback based on a <context-param>. That's a bit brutish in my opinion, and probably a hard sell to my fellow dev team when log4j "just works" in this situation.
I'm far from a Maven expert, so I'm hoping there is some elegant way to get Maven to help me here. Or perhaps some logback extension or add-on that makes it more web-app friendly. Or even a clever idea that I haven't thought of.
There are a number of possible solutions, but the easiest is to put the file in its own module and mark the dependency as provided. The, conspire to have it on the classpath when running the standalone version of the app.
The solution that we ended up using was to leave only the common "non-app" pieces (code and configuration) in core and then extract the other "app" pieces into a new module (batch-app).
The logging configuration only lives in the 2 app projects (webapp and batch-app) that depend on core. core has a logback-test.xml configuration in it, but that's excluded from the JAR that maven builds (since it's in the src/test/resources folder).

Runtime dependency (e.g. connection pooling) and classpath?

I have a Maven 3 project that uses Hibernate 3. In the Hibernate properties file, there is an entry for hibernate.connection.provider_class with the class corresponding to the C3P0 connection provider (org.hibernate.connection.C3P0ConnectionProvider). Obviously, this class is only used at runtime, so I don't need to add the corresponding dependency in my POM with the compile scope. Now, I want to give the possibility to use any connection pooling framework desired, so I also don't add a runtime dependency to the POM.
What is the best practice?
I thought about adding an entry to the classpath corresponding to the runtime dependency (in this case, hibernate-c3p0) when the application is run (for example, using the command line). But, I don't know if it's possible.
This is almost (maybe exactly) the same problem as with SLF4J. I don't know if Hibernate also uses the facade pattern for connection pooling.
Thanks
Since your code doesn't depend on the connection pooling (neither the main code nor the tests need it), there is no point to mention the dependency anywhere.
If anyone should mention it, then that would be Hibernate because Hibernate offers this feature in its config.
But you can add it to your POM with optional: true to indicate:
I support this feature
If you use it, then I recommend this framework and this version
That will make life slightly more simple for consumers of your project.
But overall, you should not mention features provided/needed by other projects unless they have some impact on your code (like when you offer a more simple way to configure connection pooling for Hibernate).
[EDIT] Your main concern is probably how to configure the project for QA. The technical term for this new movement is "DevOps" - instead of producing a dump WAR which the customer (QA) has to configure painstakingly, configuration is part of the development process just like everything else. What you pass on is a completely configured, ready-to-run setup.
To implement this, create another Maven module called "project-qa" which depends on your project and everything else you need to turn the dead code into a running application (so it will depend on DBCP plus it will contain all the necessary config files).
Maven supports overlayed WARs which will allow you to implement this painlessly.
You can mark your dependency as optional. In this case it will not be packaged into archives. In this case you have to ensure that your container provides required library.
You could use a different profile for each connection provider. In each profile you put the runtime dependency that correspond to the connection provider you want to use and change the hibernate.connection.provider_class property accordingly.
For more details about how to configure dependencies in profiles, see Different dependencies for different build profiles in maven.
To see how to change the value of the hibernate.connection.provider_class property see How can I change a .properties file in maven depending on my profile?

Resources