diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JacksonRuntimeHints.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JacksonRuntimeHints.java new file mode 100644 index 0000000000..fb3012856d --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JacksonRuntimeHints.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.json; + +import java.util.stream.Stream; + +import com.fasterxml.jackson.databind.ser.std.ClassSerializer; +import com.fasterxml.jackson.databind.ser.std.FileSerializer; +import com.fasterxml.jackson.databind.ser.std.StdJdkSerializers.AtomicBooleanSerializer; +import com.fasterxml.jackson.databind.ser.std.StdJdkSerializers.AtomicIntegerSerializer; +import com.fasterxml.jackson.databind.ser.std.StdJdkSerializers.AtomicLongSerializer; +import com.fasterxml.jackson.databind.ser.std.TokenBufferSerializer; + +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.ReflectionHints; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.util.ClassUtils; + +/** + * {@link RuntimeHintsRegistrar} for Jackson. + * + * @author Moritz Halbritter + */ +class JacksonRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + if (!ClassUtils.isPresent("com.fasterxml.jackson.databind.ser.BasicSerializerFactory", classLoader)) { + return; + } + registerSerializers(hints.reflection()); + } + + private void registerSerializers(ReflectionHints hints) { + Stream.of(AtomicBooleanSerializer.class, AtomicIntegerSerializer.class, AtomicLongSerializer.class, + FileSerializer.class, ClassSerializer.class, TokenBufferSerializer.class) + .forEach((type) -> hints.registerType(type, + (hint) -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS))); + } + +} diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories index bb6820f0a7..2c2d7468b6 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories @@ -1,9 +1,10 @@ org.springframework.aot.hint.RuntimeHintsRegistrar=\ org.springframework.boot.context.config.ConfigDataLocationRuntimeHintsRegistrar,\ org.springframework.boot.env.PropertySourceRuntimeHintsRegistrar,\ +org.springframework.boot.json.JacksonRuntimeHints,\ +org.springframework.boot.logging.java.JavaLoggingSystemRuntimeHints,\ org.springframework.boot.logging.logback.LogbackRuntimeHintsRegistrar,\ -org.springframework.boot.WebApplicationType.WebApplicationTypeRuntimeHintsRegistrar,\ -org.springframework.boot.logging.java.JavaLoggingSystemRuntimeHints +org.springframework.boot.WebApplicationType.WebApplicationTypeRuntimeHintsRegistrar org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\ org.springframework.boot.context.properties.ConfigurationPropertiesBeanFactoryInitializationAotProcessor diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JacksonRuntimeHintsTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JacksonRuntimeHintsTests.java new file mode 100644 index 0000000000..3585380d79 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JacksonRuntimeHintsTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.json; + +import java.util.Set; + +import com.fasterxml.jackson.databind.ser.std.ClassSerializer; +import com.fasterxml.jackson.databind.ser.std.FileSerializer; +import com.fasterxml.jackson.databind.ser.std.StdJdkSerializers.AtomicBooleanSerializer; +import com.fasterxml.jackson.databind.ser.std.StdJdkSerializers.AtomicIntegerSerializer; +import com.fasterxml.jackson.databind.ser.std.StdJdkSerializers.AtomicLongSerializer; +import com.fasterxml.jackson.databind.ser.std.TokenBufferSerializer; +import org.junit.jupiter.api.Test; + +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.ReflectionHints; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.TypeHint; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JacksonRuntimeHints}. + * + * @author Moritz Halbritter + */ +class JacksonRuntimeHintsTests { + + @Test + void shouldRegisterSerializerConstructors() { + ReflectionHints hints = registerHints(); + var serializers = Set.of(AtomicBooleanSerializer.class, AtomicIntegerSerializer.class, + AtomicLongSerializer.class, FileSerializer.class, ClassSerializer.class, TokenBufferSerializer.class); + for (var serializer : serializers) { + TypeHint typeHint = hints.getTypeHint(serializer); + assertThat(typeHint).withFailMessage(() -> "No hints found for serializer " + serializer).isNotNull(); + Set memberCategories = typeHint.getMemberCategories(); + assertThat(memberCategories).containsExactly(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS); + } + } + + private ReflectionHints registerHints() { + RuntimeHints hints = new RuntimeHints(); + new JacksonRuntimeHints().registerHints(hints, getClass().getClassLoader()); + return hints.reflection(); + } + +}