Add reflection hints for PropertyNamingStrategies

See gh-33080
This commit is contained in:
Ralf Ueberfuhr 2022-11-09 07:17:20 +01:00 committed by Phillip Webb
parent 50bb317215
commit e9bca3e6de
3 changed files with 97 additions and 1 deletions

View File

@ -0,0 +1,54 @@
package org.springframework.boot.autoconfigure.jackson;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.stream.Stream;
/**
* {@link RuntimeHintsRegistrar} for Jackson.
* This is necessary for the configuration of Jackson's propertyNamingStrategy
* to work in native build and execution.
*
* @author Ralf Ueberfuhr
*/
class JacksonRuntimeHints implements RuntimeHintsRegistrar {
/*
* We need this for
*
* JacksonAutoConfiguration
* .Jackson2ObjectMapperBuilderCustomizerConfiguration
* .StandardJackson2ObjectMapperBuilderCustomizer
* .configurePropertyNamingStrategyField(...)
*
* to get the field using reflection!
*/
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
if(ClassUtils.isPresent("com.fasterxml.jackson.databind.PropertyNamingStrategy", classLoader)) {
registerHints(hints.reflection());
}
}
private void registerHints(ReflectionHints reflection) {
Field[] fieldsOfStrategies = PropertyNamingStrategies.class.getDeclaredFields();
// Jackson 2.12 pre
Field[] fieldsOfStrategy = PropertyNamingStrategy.class.getDeclaredFields();
// Find all static fields that provide a PropertyNamingStrategy
// (this way we automatically support new constants
// that may be added by Jackson in the future)
Stream.concat(Stream.of(fieldsOfStrategies), Stream.of(fieldsOfStrategy))
.filter(f -> Modifier.isStatic(f.getModifiers()))
.filter(f -> f.getType().isAssignableFrom(PropertyNamingStrategy.class))
.forEach(reflection::registerField);
}
}

View File

@ -1,5 +1,6 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.springframework.boot.autoconfigure.template.TemplateRuntimeHints
org.springframework.boot.autoconfigure.template.TemplateRuntimeHints,\
org.springframework.boot.autoconfigure.jackson.JacksonRuntimeHints
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingProcessor

View File

@ -0,0 +1,41 @@
package org.springframework.boot.autoconfigure.jackson;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.predicate.ReflectionHintsPredicates;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
class JacksonRuntimeHintsTests {
@Test
void shouldRegisterHints() {
shouldRegisterFieldHintsFor(PropertyNamingStrategies.class,
"LOWER_CAMEL_CASE", "UPPER_CAMEL_CASE",
"SNAKE_CASE", "UPPER_SNAKE_CASE",
"LOWER_CASE", "KEBAB_CASE", "LOWER_DOT_CASE");
}
@Test
void shouldRegisterJackson_2_12_pre_Hints() {
shouldRegisterFieldHintsFor(PropertyNamingStrategy.class,
"LOWER_CAMEL_CASE", "UPPER_CAMEL_CASE",
"SNAKE_CASE",
"LOWER_CASE", "KEBAB_CASE", "LOWER_DOT_CASE");
}
private void shouldRegisterFieldHintsFor(Class<?> clazz, String... fieldNames) {
RuntimeHints hints = new RuntimeHints();
new JacksonRuntimeHints().registerHints(hints, getClass().getClassLoader());
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
Stream.of(fieldNames)
.map(name -> reflection.onField(clazz, name))
.forEach(predicate -> assertThat(predicate).accepts(hints));
}
}