The RuntimeHints API now has quite a bit of use across the portfolio and a few common patterns are starting to emerge. We might be able to refactor the registration API to make a few of these common patterns easier.
Here are some of the common calls that we're seeing:
Introspect vs Invoke methods
Calling registerMethod allows introspection and invocation hints to be added. The following calls are quite common but it's not always obvious what they will do:
hints.reflection().registerMethod(method); // registers with invoke
hints.reflection().registerMethod(method, hint -> {}); // registers with introspect
hints.reflection().registerMethod(method, hint -> {hint.withMode(ExecutableMode.INTROSPECT)}); // registers with introspect
hints.reflection().registerMethod(method, hint -> {hint.withMode(ExecutableMode.INVOKE)}); // registers with invoke
We're quite often seeing the hint builder lambda being pulled into a static final to aid readability:
hints.reflection().registerMethod(method, INTROSPECT);
hints.reflection().registerMethod(method, INVOKE);
Additionally, the ExecutableHint class uses a List of ExecutableMode values, but since INVOKE also implies INTROSPECT this should not be necessary.
Read vs Write
Similar to above, the registerField method doesn't immediately indicate if reading or writing is allowed:
hints.reflection().registerField(field); // Allows write
hints.reflection().registerField(field, hint -> {}); // Allows read
TypeReference.of()
Often the API expects a TypeReference parameter but the user has either a Class<?> or a String. This results in a lot of calls to TypeReference.of in the caller code:
hints.reflection().registerType(TypeReference.of(type), hint -> {});
Applying the same hints to multiple types
It's appears that often the same hints need to be applied to multiple types. For example in MessagingAnnotationsRuntimeHints and JacksonRuntimeHints.
Often the caller uses a Stream to do this:
Arrays.stream(type1, type2, type3).forEach(type -> hints.reflection()....);
Reflective method lookups
Slightly less common, but still used now and again is calling ReflectionUtils to lookup a Method so that it can be passed to the API. A (now outdated) example can be found in CloudFoundryWebEndpointServletHandlerMapping.
Method linksMethod = ReflectionUtils.findMethod(CloudFoundryLinksHandler.class, "links",
HttpServletRequest.class, HttpServletResponse.class);
Assert.state(linksMethod != null, "Unable to find 'links' method");
hints.reflection().registerMethod(linksMethod);
Similarly, withMethod calls often need to create a List for the parameter types. Sometimes Collections.emptyList() is used, sometimes a Stream is used with a mapping function to convert to a TypeReference.
See InstrumentedMethodTests for some examples.
hints.reflection().registerType(String.class, typeHint -> typeHint.withMethod("toString", Collections.emptyList(), methodHint -> methodHint.setModes(ExecutableMode.INTROSPECT)));
Naming consistency
As the API has been expanded some naming inconsistencies have crept in. For example, ProxyHints refers to "JDK proxies", where as SerializationHints refers to "Java serialization". The ReflectionHints class provides a typeHints() Stream, where as most of the other APIs don't end with hint (e.g. SerializationHints.javaSerialization().
Resource include/excludes
The ResourcePatternHints is a container for includes and excludes and multiple ResourcePatternHints can be registered. It's possible that callers may expect that these includes and excludes are related, however, when the JSON is written all excludes and all includes are included together. See ResourceHintsPredicates.AggregatedResourcePatternHints for an example of the flattening that needs to occur when using the pattern hints.
The
RuntimeHintsAPI now has quite a bit of use across the portfolio and a few common patterns are starting to emerge. We might be able to refactor the registration API to make a few of these common patterns easier.Here are some of the common calls that we're seeing:
Introspect vs Invoke methods
Calling
registerMethodallows introspection and invocation hints to be added. The following calls are quite common but it's not always obvious what they will do:We're quite often seeing the hint builder lambda being pulled into a
static finalto aid readability:Additionally, the
ExecutableHintclass uses aListofExecutableModevalues, but sinceINVOKEalso impliesINTROSPECTthis should not be necessary.Read vs Write
Similar to above, the
registerFieldmethod doesn't immediately indicate if reading or writing is allowed:TypeReference.of()
Often the API expects a
TypeReferenceparameter but the user has either aClass<?>or aString. This results in a lot of calls toTypeReference.ofin the caller code:Applying the same hints to multiple types
It's appears that often the same hints need to be applied to multiple types. For example in MessagingAnnotationsRuntimeHints and JacksonRuntimeHints.
Often the caller uses a
Streamto do this:Reflective method lookups
Slightly less common, but still used now and again is calling
ReflectionUtilsto lookup aMethodso that it can be passed to the API. A (now outdated) example can be found in CloudFoundryWebEndpointServletHandlerMapping.Similarly,
withMethodcalls often need to create aListfor the parameter types. SometimesCollections.emptyList()is used, sometimes aStreamis used with a mapping function to convert to aTypeReference.See InstrumentedMethodTests for some examples.
Naming consistency
As the API has been expanded some naming inconsistencies have crept in. For example,
ProxyHintsrefers to "JDK proxies", where asSerializationHintsrefers to "Java serialization". TheReflectionHintsclass provides atypeHints()Stream, where as most of the other APIs don't end withhint(e.g.SerializationHints.javaSerialization().Resource include/excludes
The
ResourcePatternHintsis a container forincludesandexcludesand multipleResourcePatternHintscan be registered. It's possible that callers may expect that these includes and excludes are related, however, when the JSON is written all excludes and all includes are included together. See ResourceHintsPredicates.AggregatedResourcePatternHints for an example of the flattening that needs to occur when using the pattern hints.