In order to compile to native images and to provide a reasonable footprint, we are currently forced to initialize classes containing ClassUtils#isPresent checks at build time. A meaningful concrete example of that requirement is provided by Spring MVC support that does not compile unless a few classes are initialized at build time, see 2b76a12b86a62cc46886e1b7f0e52ea9d256f899.

The drawback is that this build time init of WebMvcConfigurationSupport and RestTemplate forces to initialize transitively all classes used in static fields/blocks at build time as well : ClassUtils, SpringProperties, ConcurrentReferenceHashMap. Worse : for other classes with similar pattern, if they contain a static loggers, this will force to initialize Logback at build time which is a dead end in term of compatibility as shown multiple times in Spring Native.

We are working on a middle-long term solution with GraalVM to replace build time init by another mechanism, less viral, with less compatibility issues. But for time being, we need to find a solution to this issue.

A pragmatic solution would be isolate classpath checks in a per module (spring-core, spring-web) class that would be itself init at build time but with no other purpose, removing the risk of viral expension of the classes init at build time. If we take the WebMvcConfigurationSupport use case, we would have something like:

public abstract class ClasspathUtils {

    private static final boolean romePresent;
    private static final boolean jaxb2Present;
    private static final boolean jackson2Present;

    static {
        // ...
    }

    public static boolean isRomePresent() { ... }
    public static boolean isJaxb2Present() { ... }
    public static boolean isJackson2Present() { ... }
    // ...
}

Bonus point, I think I like the fact that we provide reusable methods for classpath checks without arbitrary String parameter. Implementation would potentially even not use ClassUtils to limit the classes init at build time (not sure what is would use instead, to be discussed, not a blocking point).

Any thoughts @jhoeller @snicoll @bclozel?

Comment From: philwebb

Perhaps the @Constant annotation from this branch that we discussed in the past might help? https://github.com/philwebb/scratch-graal-conditions/tree/annotations

Comment From: sdeleuze

@philwebb Could you please confirm that this build time initialization of classes is just required for class level @Constant not for field level ones?

Comment From: philwebb

@sdeleuze correct, that's just for class-level use.

Comment From: sdeleuze

Good news, I have been able to leverage the updated @Constant experiment to remove totally build time initialization from Spring Framework 6 (can be used for portfolio projects and Boot as well) while keeping the same benefits in term of build time code removal, see this related WIP branch. Thanks a lot @philwebb!

As soon as GraalVM team provides a builtin solution via the working group @bclozel and I have joined, it would replace this GraalVM feature. That's why I have preferred not introducing a new annotation like @Constant but just target a set of patterns. Since we don't have a real idea of the timeframe, I think the GraalVM feature provide a reasonable path for this transition period.

Comment From: sdeleuze

https://github.com/sdeleuze/build-time-constant-fields allows to test it works as expected. Not sure yet if/how we should integrate that in the CI, but probably good enough for this milestone to have such side repository to test the behavior.

Logs printed during the native build allow to identify which fields are set to a constant value at build time.