spring-kafka JsonDeserializer 源码

  • 2022-08-16
  • 浏览 (471)

spring-kafka JsonDeserializer 代码

文件路径:/spring-kafka/src/main/java/org/springframework/kafka/support/serializer/JsonDeserializer.java

/*
 * Copyright 2015-2021 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.kafka.support.serializer;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;

import org.apache.kafka.common.errors.SerializationException;
import org.apache.kafka.common.header.Headers;
import org.apache.kafka.common.serialization.Deserializer;

import org.springframework.core.ResolvableType;
import org.springframework.kafka.support.JacksonUtils;
import org.springframework.kafka.support.mapping.AbstractJavaTypeMapper;
import org.springframework.kafka.support.mapping.DefaultJackson2JavaTypeMapper;
import org.springframework.kafka.support.mapping.Jackson2JavaTypeMapper;
import org.springframework.kafka.support.mapping.Jackson2JavaTypeMapper.TypePrecedence;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.type.TypeFactory;

/**
 * Generic {@link org.apache.kafka.common.serialization.Deserializer Deserializer} for
 * receiving JSON from Kafka and return Java objects.
 * <p>
 * IMPORTANT: Configuration must be done completely with property setters or via
 * {@link #configure(Map, boolean)}, not a mixture. If any setters have been called,
 * {@link #configure(Map, boolean)} will be a no-op.
 *
 * @param <T> class of the entity, representing messages
 *
 * @author Igor Stepanov
 * @author Artem Bilan
 * @author Gary Russell
 * @author Yanming Zhou
 * @author Elliot Kennedy
 * @author Torsten Schleede
 * @author Ivan Ponomarev
 */
public class JsonDeserializer<T> implements Deserializer<T> {

	/**
	 * Kafka config property for the default key type if no header.
	 */
	public static final String KEY_DEFAULT_TYPE = "spring.json.key.default.type";

	/**
	 * Kafka config property for the default value type if no header.
	 */
	public static final String VALUE_DEFAULT_TYPE = "spring.json.value.default.type";

	/**
	 * Kafka config property for trusted deserialization packages.
	 */
	public static final String TRUSTED_PACKAGES = "spring.json.trusted.packages";

	/**
	 * Kafka config property to add type mappings to the type mapper:
	 * 'foo=com.Foo,bar=com.Bar'.
	 */
	public static final String TYPE_MAPPINGS = JsonSerializer.TYPE_MAPPINGS;

	/**
	 * Kafka config property for removing type headers (default true).
	 */
	public static final String REMOVE_TYPE_INFO_HEADERS = "spring.json.remove.type.headers";

	/**
	 * Kafka config property for using type headers (default true).
	 * @since 2.2.3
	 */
	public static final String USE_TYPE_INFO_HEADERS = "spring.json.use.type.headers";

	/**
	 * A method name to determine the {@link JavaType} to deserialize the key to:
	 * 'com.Foo.deserialize'. See {@link JsonTypeResolver#resolveType} for the signature.
	 */
	public static final String KEY_TYPE_METHOD = "spring.json.key.type.method";

	/**
	 * A method name to determine the {@link JavaType} to deserialize the value to:
	 * 'com.Foo.deserialize'. See {@link JsonTypeResolver#resolveType} for the signature.
	 */
	public static final String VALUE_TYPE_METHOD = "spring.json.value.type.method";

	private static final Set<String> OUR_KEYS = new HashSet<>();

	static {
		OUR_KEYS.add(KEY_DEFAULT_TYPE);
		OUR_KEYS.add(VALUE_DEFAULT_TYPE);
		OUR_KEYS.add(TRUSTED_PACKAGES);
		OUR_KEYS.add(TYPE_MAPPINGS);
		OUR_KEYS.add(REMOVE_TYPE_INFO_HEADERS);
		OUR_KEYS.add(USE_TYPE_INFO_HEADERS);
		OUR_KEYS.add(KEY_TYPE_METHOD);
		OUR_KEYS.add(VALUE_TYPE_METHOD);
	}

	protected final ObjectMapper objectMapper; // NOSONAR

	protected JavaType targetType; // NOSONAR

	protected Jackson2JavaTypeMapper typeMapper = new DefaultJackson2JavaTypeMapper(); // NOSONAR

	private ObjectReader reader;

	private boolean typeMapperExplicitlySet = false;

	private boolean removeTypeHeaders = true;

	private boolean useTypeHeaders = true;

	private JsonTypeResolver typeResolver;

	private boolean setterCalled;

	private boolean configured;

	/**
	 * Construct an instance with a default {@link ObjectMapper}.
	 */
	public JsonDeserializer() {
		this((Class<T>) null, true);
	}

	/**
	 * Construct an instance with the provided {@link ObjectMapper}.
	 * @param objectMapper a custom object mapper.
	 */
	public JsonDeserializer(ObjectMapper objectMapper) {
		this((Class<T>) null, objectMapper, true);
	}

	/**
	 * Construct an instance with the provided target type, and a default
	 * {@link ObjectMapper}.
	 * @param targetType the target type to use if no type info headers are present.
	 */
	public JsonDeserializer(@Nullable Class<? super T> targetType) {
		this(targetType, true);
	}

	/**
	 * Construct an instance with the provided target type, and a default {@link ObjectMapper}.
	 * @param targetType the target type reference to use if no type info headers are present.
	 * @since 2.3
	 */
	public JsonDeserializer(@Nullable TypeReference<? super T> targetType) {
		this(targetType, true);
	}


	/**
	 * Construct an instance with the provided target type, and a default {@link ObjectMapper}.
	 * @param targetType the target java type to use if no type info headers are present.
	 * @since 2.3
	 */
	public JsonDeserializer(@Nullable JavaType targetType) {
		this(targetType, true);
	}

	/**
	 * Construct an instance with the provided target type, and
	 * useHeadersIfPresent with a default {@link ObjectMapper}.
	 * @param targetType the target type.
	 * @param useHeadersIfPresent true to use headers if present and fall back to target
	 * type if not.
	 * @since 2.2
	 */
	public JsonDeserializer(@Nullable Class<? super T> targetType, boolean useHeadersIfPresent) {
		this(targetType, JacksonUtils.enhancedObjectMapper(), useHeadersIfPresent);
	}

	/**
	 * Construct an instance with the provided target type, and
	 * useHeadersIfPresent with a default {@link ObjectMapper}.
	 * @param targetType the target type reference.
	 * @param useHeadersIfPresent true to use headers if present and fall back to target
	 * type if not.
	 * @since 2.3
	 */
	public JsonDeserializer(TypeReference<? super T> targetType, boolean useHeadersIfPresent) {
		this(targetType, JacksonUtils.enhancedObjectMapper(), useHeadersIfPresent);
	}

	/**
	 * Construct an instance with the provided target type, and
	 * useHeadersIfPresent with a default {@link ObjectMapper}.
	 * @param targetType the target java type.
	 * @param useHeadersIfPresent true to use headers if present and fall back to target
	 * type if not.
	 * @since 2.3
	 */
	public JsonDeserializer(JavaType targetType, boolean useHeadersIfPresent) {
		this(targetType, JacksonUtils.enhancedObjectMapper(), useHeadersIfPresent);
	}

	/**
	 * Construct an instance with the provided target type, and {@link ObjectMapper}.
	 * @param targetType the target type to use if no type info headers are present.
	 * @param objectMapper the mapper. type if not.
	 */
	public JsonDeserializer(Class<? super T> targetType, ObjectMapper objectMapper) {
		this(targetType, objectMapper, true);
	}

	/**
	 * Construct an instance with the provided target type, and {@link ObjectMapper}.
	 * @param targetType the target type reference to use if no type info headers are present.
	 * @param objectMapper the mapper. type if not.
	 */
	public JsonDeserializer(TypeReference<? super T> targetType, ObjectMapper objectMapper) {
		this(targetType, objectMapper, true);
	}

	/**
	 * Construct an instance with the provided target type, and {@link ObjectMapper}.
	 * @param targetType the target java type to use if no type info headers are present.
	 * @param objectMapper the mapper. type if not.
	 */
	public JsonDeserializer(JavaType targetType, ObjectMapper objectMapper) {
		this(targetType, objectMapper, true);
	}

	/**
	 * Construct an instance with the provided target type, {@link ObjectMapper} and
	 * useHeadersIfPresent.
	 * @param targetType the target type.
	 * @param objectMapper the mapper.
	 * @param useHeadersIfPresent true to use headers if present and fall back to target
	 * type if not.
	 * @since 2.2
	 */
	public JsonDeserializer(@Nullable Class<? super T> targetType, ObjectMapper objectMapper,
			boolean useHeadersIfPresent) {

		Assert.notNull(objectMapper, "'objectMapper' must not be null.");
		this.objectMapper = objectMapper;
		JavaType javaType = null;
		if (targetType == null) {
			Class<?> genericType = ResolvableType.forClass(getClass()).getSuperType().resolveGeneric(0);
			if (genericType != null) {
				javaType = TypeFactory.defaultInstance().constructType(genericType);
			}
		}
		else {
			javaType = TypeFactory.defaultInstance().constructType(targetType);
		}

		initialize(javaType, useHeadersIfPresent);
	}

	/**
	 * Construct an instance with the provided target type, {@link ObjectMapper} and
	 * useHeadersIfPresent.
	 * @param targetType the target type reference.
	 * @param objectMapper the mapper.
	 * @param useHeadersIfPresent true to use headers if present and fall back to target
	 * type if not.
	 * @since 2.3
	 */
	public JsonDeserializer(TypeReference<? super T> targetType, ObjectMapper objectMapper,
			boolean useHeadersIfPresent) {

		this(targetType != null ? TypeFactory.defaultInstance().constructType(targetType) : null,
				objectMapper, useHeadersIfPresent);
	}

	/**
	 * Construct an instance with the provided target type, {@link ObjectMapper} and
	 * useHeadersIfPresent.
	 * @param targetType the target type reference.
	 * @param objectMapper the mapper.
	 * @param useHeadersIfPresent true to use headers if present and fall back to target
	 * type if not.
	 * @since 2.3
	 */
	public JsonDeserializer(@Nullable JavaType targetType, ObjectMapper objectMapper,
			boolean useHeadersIfPresent) {

		Assert.notNull(objectMapper, "'objectMapper' must not be null.");
		this.objectMapper = objectMapper;
		initialize(targetType, useHeadersIfPresent);
	}

	public Jackson2JavaTypeMapper getTypeMapper() {
		return this.typeMapper;
	}

	/**
	 * Set a customized type mapper. If the mapper is an {@link AbstractJavaTypeMapper},
	 * any class mappings configured in the mapper will be added to the trusted packages.
	 * @param typeMapper the type mapper.
	 * @since 2.1
	 */
	public void setTypeMapper(Jackson2JavaTypeMapper typeMapper) {
		Assert.notNull(typeMapper, "'typeMapper' cannot be null");
		this.typeMapper = typeMapper;
		this.typeMapperExplicitlySet = true;
		if (typeMapper instanceof AbstractJavaTypeMapper) {
			addMappingsToTrusted(((AbstractJavaTypeMapper) typeMapper).getIdClassMapping());
		}
		this.setterCalled = true;
	}

	/**
	 * Configure the default Jackson2JavaTypeMapper to use key type headers.
	 * @param isKey Use key type headers if true
	 * @since 2.1.3
	 */
	public void setUseTypeMapperForKey(boolean isKey) {
		doSetUseTypeMapperForKey(isKey);
		this.setterCalled = true;
	}

	private void doSetUseTypeMapperForKey(boolean isKey) {
		if (!this.typeMapperExplicitlySet
				&& this.getTypeMapper() instanceof AbstractJavaTypeMapper) {
			((AbstractJavaTypeMapper) this.getTypeMapper()).setUseForKey(isKey);
		}
	}

	/**
	 * Set to false to retain type information headers after deserialization.
	 * Default true.
	 * @param removeTypeHeaders true to remove headers.
	 * @since 2.2
	 */
	public void setRemoveTypeHeaders(boolean removeTypeHeaders) {
		this.removeTypeHeaders = removeTypeHeaders;
		this.setterCalled = true;
	}

	/**
	 * Set to false to ignore type information in headers and use the configured
	 * target type instead.
	 * Only applies if the preconfigured type mapper is used.
	 * Default true.
	 * @param useTypeHeaders false to ignore type headers.
	 * @since 2.2.8
	 */
	public void setUseTypeHeaders(boolean useTypeHeaders) {
		if (!this.typeMapperExplicitlySet) {
			this.useTypeHeaders = useTypeHeaders;
			setUpTypePrecedence(Collections.emptyMap());
		}
		this.setterCalled = true;
	}

	/**
	 * Set a {@link BiFunction} that receives the data to be deserialized and the headers
	 * and returns a JavaType.
	 * @param typeFunction the function.
	 * @since 2.5
	 */
	public void setTypeFunction(BiFunction<byte[], Headers, JavaType> typeFunction) {
		this.typeResolver = (topic, data, headers) -> typeFunction.apply(data, headers);
		this.setterCalled = true;
	}

	/**
	 * Set a {@link JsonTypeResolver} that receives the data to be deserialized and the headers
	 * and returns a JavaType.
	 * @param typeResolver the resolver.
	 * @since 2.5.3
	 */
	public void setTypeResolver(JsonTypeResolver typeResolver) {
		this.typeResolver = typeResolver;
		this.setterCalled = true;
	}

	@Override
	public synchronized void configure(Map<String, ?> configs, boolean isKey) {
		if (this.configured) {
			return;
		}
		Assert.state(!this.setterCalled || !configsHasOurKeys(configs),
				"JsonDeserializer must be configured with property setters, or via configuration properties; not both");
		doSetUseTypeMapperForKey(isKey);
		setUpTypePrecedence(configs);
		setupTarget(configs, isKey);
		if (configs.containsKey(TRUSTED_PACKAGES)
				&& configs.get(TRUSTED_PACKAGES) instanceof String) {
			this.typeMapper.addTrustedPackages(
					StringUtils.delimitedListToStringArray((String) configs.get(TRUSTED_PACKAGES), ",", " \r\n\f\t"));
		}
		if (configs.containsKey(TYPE_MAPPINGS) && !this.typeMapperExplicitlySet
				&& this.typeMapper instanceof AbstractJavaTypeMapper) {
			((AbstractJavaTypeMapper) this.typeMapper).setIdClassMapping(createMappings(configs));
		}
		if (configs.containsKey(REMOVE_TYPE_INFO_HEADERS)) {
			this.removeTypeHeaders = Boolean.parseBoolean(configs.get(REMOVE_TYPE_INFO_HEADERS).toString());
		}
		setUpTypeMethod(configs, isKey);
		this.configured = true;
	}

	private boolean configsHasOurKeys(Map<String, ?> configs) {
		for (String key : configs.keySet()) {
			if (OUR_KEYS.contains(key)) {
				return true;
			}
		}
		return false;
	}

	private Map<String, Class<?>> createMappings(Map<String, ?> configs) {
		Map<String, Class<?>> mappings =
				JsonSerializer.createMappings(configs.get(JsonSerializer.TYPE_MAPPINGS).toString());
		addMappingsToTrusted(mappings);
		return mappings;
	}

	private void setUpTypeMethod(Map<String, ?> configs, boolean isKey) {
		if (isKey && configs.containsKey(KEY_TYPE_METHOD)) {
			setUpTypeResolver((String) configs.get(KEY_TYPE_METHOD));
		}
		else if (!isKey && configs.containsKey(VALUE_TYPE_METHOD)) {
			setUpTypeResolver((String) configs.get(VALUE_TYPE_METHOD));
		}
	}

	private void setUpTypeResolver(String method) {
		try {
			this.typeResolver = buildTypeResolver(method);
		}
		catch (IllegalStateException e) {
			if (e.getCause() instanceof NoSuchMethodException) {
				this.typeResolver = (topic, data, headers) ->
					(JavaType) SerializationUtils.propertyToMethodInvokingFunction(
							method, byte[].class, getClass().getClassLoader()).apply(data, headers);
				return;
			}
			throw e;
		}
	}

	private void setUpTypePrecedence(Map<String, ?> configs) {
		if (!this.typeMapperExplicitlySet) {
			if (configs.containsKey(USE_TYPE_INFO_HEADERS)) {
				this.useTypeHeaders = Boolean.parseBoolean(configs.get(USE_TYPE_INFO_HEADERS).toString());
			}
			this.typeMapper.setTypePrecedence(this.useTypeHeaders ? TypePrecedence.TYPE_ID : TypePrecedence.INFERRED);
		}
	}

	private void setupTarget(Map<String, ?> configs, boolean isKey) {
		try {
			JavaType javaType = null;
			if (isKey && configs.containsKey(KEY_DEFAULT_TYPE)) {
				javaType = setupTargetType(configs, KEY_DEFAULT_TYPE);
			}
			else if (!isKey && configs.containsKey(VALUE_DEFAULT_TYPE)) {
				javaType = setupTargetType(configs, VALUE_DEFAULT_TYPE);
			}

			if (javaType != null) {
				initialize(javaType, TypePrecedence.TYPE_ID.equals(this.typeMapper.getTypePrecedence()));
			}
		}
		catch (ClassNotFoundException | LinkageError e) {
			throw new IllegalStateException(e);
		}
	}

	private void initialize(@Nullable JavaType type, boolean useHeadersIfPresent) {
		this.targetType = type;
		this.useTypeHeaders = useHeadersIfPresent;
		Assert.isTrue(this.targetType != null || useHeadersIfPresent,
				"'targetType' cannot be null if 'useHeadersIfPresent' is false");

		if (this.targetType != null) {
			this.reader = this.objectMapper.readerFor(this.targetType);
		}

		addTargetPackageToTrusted();
		this.typeMapper.setTypePrecedence(useHeadersIfPresent ? TypePrecedence.TYPE_ID : TypePrecedence.INFERRED);
	}

	private JavaType setupTargetType(Map<String, ?> configs, String key) throws ClassNotFoundException, LinkageError {
		if (configs.get(key) instanceof Class) {
			return TypeFactory.defaultInstance().constructType((Class<?>) configs.get(key));
		}
		else if (configs.get(key) instanceof String) {
			return TypeFactory.defaultInstance()
							.constructType(ClassUtils.forName((String) configs.get(key), null));
		}
		else {
			throw new IllegalStateException(key + " must be Class or String");
		}
	}

	/**
	 * Add trusted packages for deserialization.
	 * @param packages the packages.
	 * @since 2.1
	 */
	public synchronized void addTrustedPackages(String... packages) {
		doAddTrustedPackages(packages);
		this.setterCalled = true;
	}

	private void addMappingsToTrusted(Map<String, Class<?>> mappings) {
		mappings.values().forEach(clazz -> {
			String packageName = clazz.isArray()
					? clazz.getComponentType().getPackage().getName()
					: clazz.getPackage().getName();
			doAddTrustedPackages(packageName);
			doAddTrustedPackages(packageName + ".*");
		});
	}

	private void addTargetPackageToTrusted() {
		String targetPackageName = getTargetPackageName();
		if (targetPackageName != null) {
			doAddTrustedPackages(targetPackageName);
			doAddTrustedPackages(targetPackageName + ".*");
		}
	}

	private String getTargetPackageName() {
		if (this.targetType != null) {
			return ClassUtils.getPackageName(this.targetType.getRawClass()).replaceFirst("\\[L", "");
		}
		return null;
	}

	private void doAddTrustedPackages(String... packages) {
		this.typeMapper.addTrustedPackages(packages);
	}

	@Override
	public T deserialize(String topic, Headers headers, byte[] data) {
		if (data == null) {
			return null;
		}
		ObjectReader deserReader = null;
		JavaType javaType = null;
		if (this.typeResolver != null) {
			javaType = this.typeResolver.resolveType(topic, data, headers);
		}
		if (javaType == null && this.typeMapper.getTypePrecedence().equals(TypePrecedence.TYPE_ID)) {
			javaType = this.typeMapper.toJavaType(headers);
		}
		if (javaType != null) {
			deserReader = this.objectMapper.readerFor(javaType);
		}
		if (this.removeTypeHeaders) {
			this.typeMapper.removeHeaders(headers);
		}
		if (deserReader == null) {
			deserReader = this.reader;
		}
		Assert.state(deserReader != null, "No type information in headers and no default type provided");
		try {
			return deserReader.readValue(data);
		}
		catch (IOException e) {
			throw new SerializationException("Can't deserialize data [" + Arrays.toString(data) +
					"] from topic [" + topic + "]", e);
		}
	}

	@Override
	public T deserialize(String topic, @Nullable byte[] data) {
		if (data == null) {
			return null;
		}
		ObjectReader localReader = this.reader;
		if (this.typeResolver != null) {
			JavaType javaType = this.typeResolver.resolveType(topic, data, null);
			if (javaType != null) {
				localReader = this.objectMapper.readerFor(javaType);
			}
		}
		Assert.state(localReader != null, "No headers available and no default type provided");
		try {
			return localReader.readValue(data);
		}
		catch (IOException e) {
			throw new SerializationException("Can't deserialize data [" + Arrays.toString(data) +
					"] from topic [" + topic + "]", e);
		}
	}

	@Override
	public void close() {
		// No-op
	}

	/**
	 * Copies this deserializer with same configuration, except new target type is used.
	 * @param newTargetType type used for when type headers are missing, not null
	 * @param <X> new deserialization result type
	 * @return new instance of deserializer with type changes
	 * @since 2.6
	 */
	public <X> JsonDeserializer<X> copyWithType(Class<? super X> newTargetType) {
		return copyWithType(this.objectMapper.constructType(newTargetType));
	}

	/**
	 * Copies this deserializer with same configuration, except new target type reference is used.
	 * @param newTargetType type reference used for when type headers are missing, not null
	 * @param <X> new deserialization result type
	 * @return new instance of deserializer with type changes
	 * @since 2.6
	 */
	public <X> JsonDeserializer<X> copyWithType(TypeReference<? super X> newTargetType) {
		return copyWithType(this.objectMapper.constructType(newTargetType.getType()));
	}

	/**
	 * Copies this deserializer with same configuration, except new target java type is used.
	 * @param newTargetType java type used for when type headers are missing, not null
	 * @param <X> new deserialization result type
	 * @return new instance of deserializer with type changes
	 * @since 2.6
	 */
	public <X> JsonDeserializer<X> copyWithType(JavaType newTargetType) {
		JsonDeserializer<X> result = new JsonDeserializer<>(newTargetType, this.objectMapper, this.useTypeHeaders);
		result.removeTypeHeaders = this.removeTypeHeaders;
		result.typeMapper = this.typeMapper;
		result.typeMapperExplicitlySet = this.typeMapperExplicitlySet;
		return result;
	}

	// Fluent API

	/**
	 * Designate this deserializer for deserializing keys (default is values); only
	 * applies if the default type mapper is used.
	 * @return the deserializer.
	 * @since 2.3
	 */
	public JsonDeserializer<T> forKeys() {
		setUseTypeMapperForKey(true);
		return this;
	}

	/**
	 * Don't remove type information headers.
	 * @return the deserializer.
	 * @since 2.3
	 * @see #setRemoveTypeHeaders(boolean)
	 */
	public JsonDeserializer<T> dontRemoveTypeHeaders() {
		setRemoveTypeHeaders(false);
		return this;
	}

	/**
	 * Ignore type information headers and use the configured target class.
	 * @return the deserializer.
	 * @since 2.3
	 * @see #setUseTypeHeaders(boolean)
	 */
	public JsonDeserializer<T> ignoreTypeHeaders() {
		setUseTypeHeaders(false);
		return this;
	}

	/**
	 * Use the supplied {@link Jackson2JavaTypeMapper}.
	 * @param mapper the mapper.
	 * @return the deserializer.
	 * @since 2.3
	 * @see #setTypeMapper(Jackson2JavaTypeMapper)
	 */
	public JsonDeserializer<T> typeMapper(Jackson2JavaTypeMapper mapper) {
		setTypeMapper(mapper);
		return this;
	}

	/**
	 * Add trusted packages to the default type mapper.
	 * @param packages the packages.
	 * @return the deserializer.
	 * @since 2,5
	 */
	public synchronized JsonDeserializer<T> trustedPackages(String... packages) {
		Assert.isTrue(!this.typeMapperExplicitlySet, "When using a custom type mapper, set the trusted packages there");
		this.typeMapper.addTrustedPackages(packages);
		return this;
	}

	/**
	 * Set a {@link BiFunction} that receives the data to be deserialized and the headers
	 * and returns a JavaType.
	 * @param typeFunction the function.
	 * @return the deserializer.
	 * @since 2.5
	 */
	public JsonDeserializer<T> typeFunction(BiFunction<byte[], Headers, JavaType> typeFunction) {
		setTypeFunction(typeFunction);
		return this;
	}

	/**
	 * Set a {@link JsonTypeResolver} that receives the data to be deserialized and the headers
	 * and returns a JavaType.
	 * @param resolver the resolver.
	 * @return the deserializer.
	 * @since 2.5.3
	 */
	public JsonDeserializer<T> typeResolver(JsonTypeResolver resolver) {
		setTypeResolver(resolver);
		return this;
	}

	private JsonTypeResolver buildTypeResolver(String methodProperty) {
		int lastDotPosn = methodProperty.lastIndexOf('.');
		Assert.state(lastDotPosn > 1,
				"the method property needs to be a class name followed by the method name, separated by '.'");
		Class<?> clazz;
		try {
			clazz = ClassUtils.forName(methodProperty.substring(0, lastDotPosn), getClass().getClassLoader());
		}
		catch (ClassNotFoundException | LinkageError e) {
			throw new IllegalStateException(e);
		}
		String methodName = methodProperty.substring(lastDotPosn + 1);
		Method method;
		try {
			method = clazz.getDeclaredMethod(methodName, String.class, byte[].class, Headers.class);
			Assert.state(JavaType.class.isAssignableFrom(method.getReturnType()),
					method + " return type must be JavaType");
			Assert.state(Modifier.isStatic(method.getModifiers()), method + " must be static");
		}
		catch (SecurityException | NoSuchMethodException e) {
			throw new IllegalStateException(e);
		}
		return (topic, data, headers) -> {
			try {
				return (JavaType) method.invoke(null, topic, data, headers);
			}
			catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				throw new IllegalStateException(e);
			}
		};
	}

}

相关信息

spring-kafka 源码目录

相关文章

spring-kafka DelegatingByTopicDeserializer 源码

spring-kafka DelegatingByTopicSerialization 源码

spring-kafka DelegatingByTopicSerializer 源码

spring-kafka DelegatingByTypeSerializer 源码

spring-kafka DelegatingDeserializer 源码

spring-kafka DelegatingSerializer 源码

spring-kafka DeserializationException 源码

spring-kafka ErrorHandlingDeserializer 源码

spring-kafka FailedDeserializationInfo 源码

spring-kafka JsonSerde 源码

0  赞