Merge pull request #33080 from ueberfuhr

* pr/33080:
  Polish 'Add reflection hints for PropertyNamingStrategies'
  Add reflection hints for PropertyNamingStrategies

Closes gh-33080
pull/33082/head
Phillip Webb 2 years ago
commit 0e6d3697e4

@ -26,17 +26,22 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.cfg.ConstructorDetector;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
@ -75,6 +80,7 @@ import org.springframework.util.ReflectionUtils;
* @author Johannes Edmeier
* @author Phillip Webb
* @author Eddú Meléndez
* @author Ralf Ueberfuhr
* @since 1.1.0
*/
@AutoConfiguration
@ -342,4 +348,36 @@ public class JacksonAutoConfiguration {
}
static class JacksonAutoConfigurationRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
if (ClassUtils.isPresent("com.fasterxml.jackson.databind.PropertyNamingStrategy", classLoader)) {
registerPropertyNamingStrategyHints(hints.reflection());
}
}
/**
* Register hints for the {@code configurePropertyNamingStrategyField} method to
* use.
* @param hints reflection hints
*/
private void registerPropertyNamingStrategyHints(ReflectionHints hints) {
registerPropertyNamingStrategyHints(hints, PropertyNamingStrategies.class);
// PropertyNamingStrategy is used pre Jackson 2.12
registerPropertyNamingStrategyHints(hints, PropertyNamingStrategy.class);
}
private void registerPropertyNamingStrategyHints(ReflectionHints hints, Class<?> type) {
Stream.of(type.getDeclaredFields()).filter(this::isPropertyNamingStrategyField)
.forEach(hints::registerField);
}
private boolean isPropertyNamingStrategyField(Field candidate) {
return ReflectionUtils.isPublicStaticFinal(candidate)
&& candidate.getType().isAssignableFrom(PropertyNamingStrategy.class);
}
}
}

@ -1,4 +1,5 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.JacksonAutoConfigurationRuntimeHints,\
org.springframework.boot.autoconfigure.template.TemplateRuntimeHints
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\

@ -23,6 +23,7 @@ import java.time.Duration;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
@ -38,7 +39,9 @@ import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.cfg.ConstructorDetector;
@ -50,10 +53,14 @@ import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.assertj.core.api.InstanceOfAssertFactories;
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 org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.JacksonAutoConfigurationRuntimeHints;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.jackson.JsonMixin;
import org.springframework.boot.jackson.JsonMixinModule;
@ -81,6 +88,7 @@ import static org.mockito.Mockito.mock;
* @author Sebastien Deleuze
* @author Johannes Edmeier
* @author Grzegorz Poznachowski
* @author Ralf Ueberfuhr
*/
class JacksonAutoConfigurationTests {
@ -456,6 +464,22 @@ class JacksonAutoConfigurationTests {
});
}
@Test
void shouldRegisterPropertyNamingStrategyHints() {
shouldRegisterPropertyNamingStrategyHints(PropertyNamingStrategies.class, "LOWER_CAMEL_CASE",
"UPPER_CAMEL_CASE", "SNAKE_CASE", "UPPER_SNAKE_CASE", "LOWER_CASE", "KEBAB_CASE", "LOWER_DOT_CASE");
shouldRegisterPropertyNamingStrategyHints(PropertyNamingStrategy.class, "LOWER_CAMEL_CASE", "UPPER_CAMEL_CASE",
"SNAKE_CASE", "LOWER_CASE", "KEBAB_CASE", "LOWER_DOT_CASE");
}
private void shouldRegisterPropertyNamingStrategyHints(Class<?> type, String... fieldNames) {
RuntimeHints hints = new RuntimeHints();
new JacksonAutoConfigurationRuntimeHints().registerHints(hints, getClass().getClassLoader());
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
Stream.of(fieldNames).map((name) -> reflection.onField(type, name))
.forEach((predicate) -> assertThat(predicate).accepts(hints));
}
private void assertParameterNamesModuleCreatorBinding(Mode expectedMode, Class<?>... configClasses) {
this.contextRunner.withUserConfiguration(configClasses).run((context) -> {
DeserializationConfig deserializationConfig = context.getBean(ObjectMapper.class)

Loading…
Cancel
Save