TedeeReflectionHints.java
package org.sanmibuh.tedee.lock.infrastructure.secondary;
import java.util.Arrays;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import org.sanmibuh.framework.resilience.RetryProperties;
import org.sanmibuh.tedee.lock.infrastructure.TedeeProperties;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.ClassUtils;
final class TedeeReflectionHints implements RuntimeHintsRegistrar {
private static final String MODEL_PACKAGE = "com.tedee.bridge.client.model";
private static final MemberCategory[] JACKSON_CATEGORIES = {
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS,
MemberCategory.ACCESS_DECLARED_FIELDS
};
private static final MemberCategory[] VALIDATION_CATEGORIES = {
MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS
};
@Override
public void registerHints(final RuntimeHints hints, final @Nullable ClassLoader classLoader) {
final var scanner =
setResourceLoaderPITEquivalent(
new ClassPathScanningCandidateComponentProvider(false), classLoader);
scanner.addIncludeFilter(new AssignableTypeFilter(Object.class));
scanner.findCandidateComponents(MODEL_PACKAGE).stream()
.flatMap(
bd -> {
final var className = bd.getBeanClassName();
return className != null
? loadWithDeclaredClasses(className, classLoader)
: Stream.empty();
})
.forEach(type -> hints.reflection().registerType(type, JACKSON_CATEGORIES));
hints
.reflection()
.registerType(TedeeProperties.class, VALIDATION_CATEGORIES)
.registerType(RetryProperties.class, VALIDATION_CATEGORIES);
}
private ClassPathScanningCandidateComponentProvider setResourceLoaderPITEquivalent(
final ClassPathScanningCandidateComponentProvider scanner,
final @Nullable ClassLoader classLoader) {
scanner.setResourceLoader(new DefaultResourceLoader(classLoader));
return scanner;
}
private Stream<Class<?>> loadWithDeclaredClasses(
final String className, final @Nullable ClassLoader loader) {
final var type = ClassUtils.resolveClassName(className, loader);
return Stream.concat(Stream.of(type), Arrays.stream(type.getDeclaredClasses()));
}
}