Uncategorized

This Month in Spring – December 2021

Hi, Spring fans! Welcome to another installment of This Month in Spring! Can you believe we're staring down 2022? It's already almost the end of the year! I can't believe we quickly got this far, but we did.

There have been several things I want to draw your attention to since last month, not least of which is the information about Log4j2 and the new Spring Native release.

Log4j2 is the gift that keeps on giving

Log4j2 isn't a problem. I am so grateful to all the developers who work on Log4j2 so that we can use it. Vulnerabilities happen, and they happen to everybody. So, I wish people would try to remember that Log4j2 has served countless people just fine for a very, very long time. While there is a vulnerability, the vulnerability is easy to fix. If you're a Spring Boot developer using the default, out-of-the-box settings in a Spring Boot application, then you've got nothing to fear. I'll repeat it: unless you've explicitly opted into Log4j2, you should be fine. However, a new Logback issue could, in theory, affect Spring Boot developers. It's a much more complex vulnerability to exploit, however. That doesn't mean it's not worth your attention. Be careful out there, friends: wear a mask and patch your software!

Spring Native 0.11

Moving on to things more tantalizing: we released Spring Native 0.11, which is fantastic because it features a brand new AOT (ahead-of-time) engine that completely reworks how we transpile Spring Boot applications into GraalVM native images. I've been working with GraalVM a lot over the last two years, and this new release is a vast, revolutionary step in the story of Spring Native and a giant leap forward on the journey to Spring Framework 6 and Spring Boot 3, both of which will land next year.

I've been tinkering with the new release a lot over the last month, too. Spring Native works well for a ton of use cases supported by Spring itself, and so, for most applications, I've found things work just fine with no changes. That said, some things will not work in a Spring Native context or any GraalVM context without some help. For example, it would help if you told GraalVM what you're doing that might confound it – proxies, serialization, resource-loading, etc. Spring Native provides a mechanism – hints – by which you can do this. It's easy. But it still has to be done. So, I've been going around to some projects that I think would probably need some help and trying to make them work.

MyBatis and Spring Native

I got Spring and MyBatis to work well and put that in a sample branch. See this blog for more on that. It was challenging to get Spring and the MyBatis Spring Boot autoconfiguration to work. I started rebuilding the autoconfiguration, bit by bit, and managed to build an inarguably less helpful, less robust Spring Boot autoconfiguration for MyBatis that works well with Spring Native, too. I'm already talking to some folks on the MyBatis team about possibly including some of this work. (Fingers crossed!). With this proof-of-concept Spring Boot autoconfiguration and Spring Native configuration, you can create a MyBatis SQL Mapper like this:

@Mapper
public interface CityMapper {

   @Insert("INSERT INTO city (name, state, country) VALUES(#{name}, #{state}, #{country})")
   @Options(useGeneratedKeys = true, keyProperty = "id")
   void insert(City city);

   @Select("SELECT id, name, state, country FROM city ")
   Collection findAll();
}  

Spring Retrosocket and Spring Native

I also updated the Spring Retrosocket project to work with Spring Native. Spring Retrosocket is a declarative Feign– or Retrofit-like client for RSocket-based services.

@RSocketClient
interface GreetingsClient {

   @MessageMapping("hello")
   Mono hello(Mono name);
} 

The Kubernetes Java Client and Spring Native

Then, I turned my attention to making the Kubernetes Java client work well in a Spring Native and GraalVM context. The Kubernetes Java client is essential if you want to build memory-efficient controllers and operators for Kubernetes. Did I mention that GraalVM native images are very memory efficient? It depends on what you're doing in your application, of course, but my typical applications end up taking anywhere from 40 to 55 megabytes of RAM (well, RSS, specifically). And that's in addition to startup in only tens of milliseconds. The official Kubernetes-for-Java client has a Spring Boot autoconfiguration. So all I had to do was write the trivial Spring Native configuration required to make such applications work well in a Spring Native and GraalVM context. I explain it here in detail. Suffice it to say it's now possible to use your favorite development framework not just to build excellent Kubernetes resources and controllers but deploy them in a low-footprint fashion to your organization's clusters.

Spring GraphQL and Spring Native

Then, I turned my attention to Spring GraphQl and Spring Native. Spring GraphQL works pretty well as long as you override how the GraphQlSourceBuilder derives the Spring Framework Resource instances used to feed the engine the schema for your GraphQL endpoints. It's not as easy as it could be, but it's still only an extra @Bean or two or so lines of code to make it work. Fine. The trouble starts when you're using Spring GraphQL and want to query the metamodel for Spring GraphQL's schema itself. Having the GraphQL metamodel is convenient when, for example, you're using the /graphiql/ interactive console to query the data. That took some doing, but I did it. I explain that further in this post.

With that, I can deploy a GraphQL controller like this:

@Controller
class CustomerGraphQlController {

   private final CustomerRepository repository; 
   CustomerGraphQlController(CustomerRepository repository) {
      this.repository = repository ;
   } 
   @QueryMapping
   Flux customers() {
    return this.repository.findAll();  
   }
}

record Customer(@Id Integer id, String name) { } 

…that uses the following schema:

type Query { customers : [Customer] } type Customer { id: ID name :String } 

Then open the example up at http://localhost:8080/graphiql/ and issue the following query:

query { customers { id, name } } 

And get the results I was expecting!

Miscellaneous

I've also been dealing with many other things I wanted to get working with Spring Native. So here's the Spring Native configuration for CommonMark, a Markdown parser in Java.

Here are the various classes I had to add to make Apache Lucene work in a Spring Native project. Of course, this example is more involved and uses GraalVM substitutions and a typical Spring Native configuration. But it works, and well, too!

Oh, and did I mention I worked with Ronald Dehuysser to get Jobrunr, a distributed job scheduling engine, working in a GraalVM context with Spring Native? Because I did, and the result is awesome!

I've done all this in just the last few weeks: the possibilities are endless, and I can't wait to see more and more of the wide and world of Springdom sprout GraalVM integrations.

Anyway, enough of this ever-so-indulgent discussion of building GraalVM and native applications; we've got a ton of good stuff to review this month, so let's dive into it!