ServiceLoader for TypeAdapterFactory
- Dominant language
- Java
- Stars
- 24.2k
- Forks
- 4.5k
- Avg merge
- 6d 4h
- Merged PRs (30d)
- 12
Description
### Idea
Would it make sense to add a ServiceLoader ([Java 6 doc](https://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html), [Java 11 doc](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html)) so that applications can register their service providers for `TypeAdapterFactory` which then affects all `Gson` instances?
The use-case I am thinking of is providing consistent (de-)serialization for collection libraries.
E.g. let's say I am developing a library which contains a `MultiMap` (mapping 1 key -> N values).
A sensible serialization would be:
```
{
"key": ["value1", "value2", ...],
...
}
```
Using the `@JsonAdapter` annotation might not be an option because it would require a dependency on Gson. Therefore a separate project for the type adapters makes sense, however users of that project still have to register the type adapters for their `Gson` instance which is cumbersome and error-prone in case they forget it.
With a service loader, that project could simply define in `META-INF/services/com.google.gson.TypeAdapterFactory` the factories their project provides. This way the user would not have to manually register the appropriate adapters, but they would be used automatically (given that the project is used as dependency).
What do you think?
### Considerations
- This feature should then only be used by authors of the respective collection library and not by third-parties (this should be mentioned in the Java doc). Otherwise one might for example end up with 3 different implementations for (de-serializing) Guava's Multimap, which would be a dependency nightmare.
- TypeAdapterFactories provided through the service loader approach should have the lowest priority, all manually registered TypeAdapterFactories and TypeAdapters should be considered first.
- If no TypeAdapterFactory from the service loader provides an adapter for a type, then the reflective approach should be used, as it is currently the case.
- Maybe the reflective TypeAdapterFactory has to be exposed, so it is possible to overwrite the (de-)serialization approach of one or more TypeAdapterFactory from the service loader, if necessary (e.g. to remain backwards compatible).
- Performance of this approach has to be tested. Users which do not (de-)serialize classes for which the ServiceLoader is used, but instead use the reflective TypeAdapterFactory should not suffer from this change.
- This can lead to unexpected behavior when TypeAdapterFactory service implementations are applied unintentionally:
- When a (deeply nested) transitive dependency adds an artifact with service implementations, and then suddenly the serialization format of the application changes.
- For third-party libraries which already use ServiceLoader for TypeAdapterFactory, but only expect their Gson usage to be affected, and not Gson generally. There seem to be multiple libraries which do this: https://github.com/search?q=%22ServiceLoader.load%28TypeAdapterFactory.class%29%22&type=code
Jackson provides opt-in functionality with [`ObjectMapper#findAndRegisterModules()`](https://javadoc.io/static/com.fasterxml.jackson.core/jackson-databind/2.17.2/com/fasterxml/jackson/databind/ObjectMapper.html#findAndRegisterModules--).
Though an opt-in approach would only help those which don't want this ServiceLoader functionality at all; it would not actually fix the problem for those who do want to use this ServiceLoader approach, but don't want some of the service implementations.
Though maybe then **instead of this ServiceLoader approach it would be easiest and least error-prone if libraries just provided a TypeAdapterFactory and users could then manually register it**, if desired.
Contributor guide
Assessment
This issue has not been assessed yet.