Using Jackson builders in Spring Boot Native/ / Reading Time: 5 Minute / Origin: kabisa.nl/tech
When you’ve read a few of my earlier blog posts, you know that I cherish a secret love for the builder pattern used to create immutable objects. My default implementation includes the Jackson library for (de)serialization of this pattern, and Lombok for providing the perfect glue with almost no boilerplate code.
When you’ve not read any of my earlier posts (yes, shame on you) here is the link: What I love about Lombok and the builder pattern
Only interested in the code? Look at the final gist.
What’s the problem?
When using the builder pattern with Jackson in a regular Java Virtual Machine (JVM) environment, everything is fine, everything works fine because it allows for reflection during runtime.
When using native compilation, which was recently released in Spring Boot 3 (link), you cannot rely on this reflection during runtime and need to configure it upfront during build time.
When you do not configure anything during build time, you will run into a RuntimeException that looks something like this.
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Builder class nl.vreijsenj.blog.Movie$Builder does not have build method (name: 'build')
Spring Boot 3 allows for what’s called “native hints” to be declared, which are used during build time. These Native Hints allow you to specify classes or methods that should be accessible using reflection, or specify resources that would normally be excluded by the native build but are actually needed for a specific use-case.
Spring Boot’s way of specifying these native hints is by implementing the RuntimeHints API.
Identifying Jackson Builders
We first need to be able to identify the builder classes that we need to configure reflection for.
As you know, Lombok’s
@Jacksonized annotation will put Jackson’s
@JsonPOJOBuilder annotations on the builder class which we can use to identify our builder classes.
Let’s see how that would look;
List<TypeReference> builders = getClasses(loader, ROOT_PACKAGE).stream()
We then have to instruct the native compilation process to allow invocation of the declared constructor and public methods, think about the build method from our error message.
Completing the puzzle
When we put the above into our own implementation of a
RuntimeHintsRegistrar we get the following class:
Well, there you have it, a class ready to be included in any Spring Boot 3 application.
Rather prefer a gist? Way ahead of you here is the gist.
That’s all folks! 👋