spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

Revisit SpringFactoriesLoader vs. ServiceLoader infrastructure

Open bclozel opened this issue 3 years ago • 1 comments

SpringFactoriesLoader has been added in Spring Framework 3.2 (see 988f376) as a simple and internal factory loading mechanism. At the time, the 3.x generation had a Java 1.5 baseline and could not require java.util.ServiceLoader, introduced in Java 1.6.

Over the years, this mechanism has been used more and more by many Spring projects for loading factories. Its contract has also been extended in #15158 for loading factory names and possibly instantiating them "manually" using reflection. Third party projects are also expected to use the same infrastructure if they want to provide their own support (for example, auto-configurations).

With this issue, the Spring Framework team should discuss whether we should reconsider the current situation, given that:

  1. Spring Framework 6 will have a Java 17 baseline
  2. The Java Platform Module System (JPMS) adds new constraints in this field
  3. The AOT engine (currently in Spring Native, to be ported to Spring Framework) requires specific support for that

Java 17 baseline

ServiceLoader is now mature and has been evolved over the years. The ServiceLoader::stream() method (added in Java 9) allows to iterate over the types without instantiating them. With that, we could probably replace SpringFactoriesLoader.

The Java Platform Module System (JPMS)

Again with Java 9, new mechanisms were added to ServiceLoader to help with the newly introduced module system. With the JPMS, modules must declare what they export and what they require; without that, they cannot access classes (reflectively or not) if they're not explicitly required by the module descriptor.

With ServiceLoader, any module can declare service implementations. By definition, service clients cannot be aware of all of them and are not in a position to declare them in their module descriptor. ServiceLoader has been improved with new methods and its own provides ... with ... and uses ... keywords in the module descriptor.

SpringFactoriesLoader will hit the JPMS limitations and doesn't currently have any mechanism to address it.

The Spring AOT engine

Spring Native has implemented an AOT engine that pre-processes application configuration and generates Java source files. It greatly helps the GraalVM native compilation phase by making the application startup more explicit and leaner.

Because dynamic class loading and reflection are well-known limitations with native compilation, Spring Native added a specific processing of Spring Factories at build time. This process is listing all factories and their implementations available at build time, and writes a Java source file that explicitly references them so that the GraalVM native static analysis phase discovers them.

Depending on the choice made here, the AOT engine will need to have a SpringFactoriesLoader-specific mechanism or possibly extend the existing ServiceLoader support in GraalVM native.

bclozel avatar Nov 30 '21 21:11 bclozel

@jhoeller, @sdeleuze and I have discussed this and came up with the following plan:

  1. The SpringFactoriesLoader#loadFactoryNames contract is just too broad and permissive for AOT processing and enables use cases which are outside of the original scope of this class. We're going to deprecate this method in 6.0.x and remove it completely in the 6.x timeline. See #27954
  2. SpringFactoriesLoader should be enhanced to allow for static registrations of factories, skipping the loading of spring.factories file and reflection. This should statically populate a "cache" that holds references to factory types and suppliers that instantiate factories implementations. See #27955

As part of this issue, we can revisit SpringFactoriesLoader to use ServiceLoader mechanism as one of the supported mechanisms to load factories (static loading for AOT, ServiceLoader and regular spring.factories). This should allow developers to use SpringFactoriesLoader in a JPMS scenario. Note that this issue does not consider moving the existing spring.factories declarations to ServiceLoader metadata.

bclozel avatar Jan 19 '22 17:01 bclozel

Closing this for the time being since we have no immediate need for closer alignment with the ServiceLoader mechanism.

From a core framework perspective, SpringFactoriesLoader is a rather specific mechanism for certain rarely used SPIs (such as BeanInfoFactory) that are meant to be local to the current deployment arrangement.

Broader appeal of SpringFactoriesLoader, potentially with cross-module semantics, depends on usage scenarios outside of the core framework - none of which are currently known to benefit from ServiceLoader alignment.

jhoeller avatar Oct 05 '22 16:10 jhoeller