GH-1196 Recactor registratio of Kotlin module

Resolves #1196
This commit is contained in:
Oleg Zhurakousky
2024-10-23 09:58:35 +02:00
parent 900d03f816
commit 3930cad5df
3 changed files with 29 additions and 18 deletions

View File

@@ -25,12 +25,14 @@ import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.gson.Gson;
import io.cloudevents.spring.messaging.CloudEventMessageConverter;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -59,6 +61,7 @@ import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.KotlinDetector;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.support.ConfigurableConversionService;
@@ -215,6 +218,7 @@ public class ContextFunctionCatalogAutoConfiguration {
return new GsonMapper(gson);
}
@SuppressWarnings("unchecked")
private JsonMapper jackson(ApplicationContext context) {
ObjectMapper mapper;
try {
@@ -225,6 +229,19 @@ public class ContextFunctionCatalogAutoConfiguration {
mapper.registerModule(new JavaTimeModule());
}
if (KotlinDetector.isKotlinPresent()) {
try {
if (!mapper.getRegisteredModuleIds().contains("com.fasterxml.jackson.module.kotlin.KotlinModule")) {
Class<? extends Module> kotlinModuleClass = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", ClassUtils.getDefaultClassLoader());
Module kotlinModule = BeanUtils.instantiateClass(kotlinModuleClass);
mapper.registerModule(kotlinModule);
}
}
catch (ClassNotFoundException ex) {
// jackson-module-kotlin not available
}
}
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, true);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

View File

@@ -22,7 +22,6 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import com.fasterxml.jackson.module.kotlin.KotlinModule;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
@@ -37,14 +36,10 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ResolvableType;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.ObjectUtils;
/**
@@ -62,19 +57,6 @@ public class KotlinLambdaToFunctionAutoConfiguration {
protected final Log logger = LogFactory.getLog(getClass());
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = {"org.springframework.http.converter.json.Jackson2ObjectMapperBuilder",
"com.fasterxml.jackson.module.kotlin.KotlinModule"})
Jackson2ObjectMapperBuilderCustomizer customizer() {
return new Jackson2ObjectMapperBuilderCustomizer() {
@Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.modulesToInstall(KotlinModule.class);
}
};
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static final class KotlinFunctionWrapper implements Function<Object, Object>, Supplier<Object>, Consumer<Object>,

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.function.utils;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.util.Date;
@@ -38,6 +39,7 @@ import org.springframework.cloud.function.json.JsonMapper;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ResolvableType;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -84,6 +86,16 @@ public class JsonMapperTests {
assertThat(convertedJson).contains("\"zonedDateTime\":\"2024-10-16T16:13:29.964361+02:00\"");
}
@Test
public void testKotlinModuleRegistration() throws Exception {
ApplicationContext context = SpringApplication.run(EmptyConfiguration.class);
JsonMapper jsonMapper = context.getBean(JsonMapper.class);
Field mapperField = ReflectionUtils.findField(jsonMapper.getClass(), "mapper");
mapperField.setAccessible(true);
ObjectMapper mapper = (ObjectMapper) mapperField.get(jsonMapper);
assertThat(mapper.getRegisteredModuleIds()).contains("com.fasterxml.jackson.module.kotlin.KotlinModule");
}
@ParameterizedTest
@MethodSource("params")
public void vanillaArray(JsonMapper mapper) {