diff --git a/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/CompatibleKryo.java b/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/CompatibleKryo.java index dcd000767b0..929b1867f3e 100644 --- a/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/CompatibleKryo.java +++ b/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/CompatibleKryo.java @@ -16,30 +16,70 @@ */ package org.apache.dubbo.common.serialize.kryo; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.serialize.kryo.serializer.CommonJavaSerializer; import org.apache.dubbo.common.serialize.kryo.utils.ReflectionUtils; +import sun.reflect.ReflectionFactory; -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.Serializer; -import com.esotericsoftware.kryo.serializers.JavaSerializer; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.ConcurrentHashMap; public class CompatibleKryo extends Kryo { + private static ReflectionFactory reflectionFactory = ReflectionFactory + .getReflectionFactory(); + private static final Logger logger = LoggerFactory.getLogger(CompatibleKryo.class); + private static ConcurrentHashMap, Constructor> constructorCache = new ConcurrentHashMap<>(); + @Override public Serializer getDefaultSerializer(Class type) { if (type == null) { throw new IllegalArgumentException("type cannot be null."); } + // 对于这些类型的 :array 、enum、没有默认构造器的类 if (!type.isArray() && !type.isEnum() && !ReflectionUtils.checkZeroArgConstructor(type)) { if (logger.isWarnEnabled()) { logger.warn(type + " has no zero-arg constructor and this will affect the serialization performance"); } - return new JavaSerializer(); + return new CommonJavaSerializer(); } return super.getDefaultSerializer(type); } + + @Override + public T newInstance(Class type) { + try { + return super.newInstance(type); + } catch (Exception e) { + return newInstanceByReflection(type); + } + } + + @SuppressWarnings("all") + private T newInstanceByReflection(Class type) { + Object instance = null; + try { + Constructor constructor = constructorCache.get(type); + if (constructor == null) { + constructor = reflectionFactory.newConstructorForSerialization(type, + Object.class.getDeclaredConstructor()); + constructorCache.putIfAbsent(type, constructor); + // in order to improve reflection performance + constructor.setAccessible(true); + } + instance = constructor.newInstance(); + return (T) instance; + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { + logger.error(type.getName() + "unable to be serialized", e); + e.printStackTrace(); + } + return (T) instance; + } } diff --git a/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/ExtensionKryo.java b/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/ExtensionKryo.java new file mode 100644 index 00000000000..99c3ccf934e --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/ExtensionKryo.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.common.serialize.kryo; + +import com.esotericsoftware.kryo.Serializer; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.serialize.kryo.serializer.CommonJavaSerializer; +import org.apache.dubbo.common.serialize.kryo.utils.ReflectionUtils; +import sun.reflect.ReflectionFactory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.ConcurrentHashMap; + +public class ExtensionKryo extends CompatibleKryo { + + private static ReflectionFactory reflectionFactory = ReflectionFactory + .getReflectionFactory(); + + private static final Logger logger = LoggerFactory.getLogger(ExtensionKryo.class); + + private static ConcurrentHashMap, Constructor> constructorCache = new ConcurrentHashMap<>(); + + @Override + public Serializer getDefaultSerializer(Class type) { + if (type == null) { + throw new IllegalArgumentException("type cannot be null."); + } + + // 对于这些类型的 :array 、enum、没有默认构造器的类 + if (!type.isArray() && !type.isEnum() && !ReflectionUtils.checkZeroArgConstructor(type)) { + return new CommonJavaSerializer(); + } + return super.getDefaultSerializer(type); + } + + @Override + public T newInstance(Class type) { + try { + return super.newInstance(type); + } catch (Exception e) { + return newInstanceByReflection(type); + } + } + + @SuppressWarnings("all") + private T newInstanceByReflection(Class type) { + Object instance = null; + try { + Constructor constructor = constructorCache.get(type); + if (constructor == null) { + constructor = reflectionFactory.newConstructorForSerialization(type, + Object.class.getDeclaredConstructor()); + constructorCache.putIfAbsent(type, constructor); + // in order to improve reflection performance + constructor.setAccessible(true); + } + instance = constructor.newInstance(); + return (T) instance; + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { + logger.error(type.getName() + "unable to be serialized", e); + e.printStackTrace(); + } + return (T) instance; + } +} diff --git a/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/serializer/CommonJavaSerializer.java b/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/serializer/CommonJavaSerializer.java new file mode 100644 index 00000000000..362dc5d6fbe --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/serializer/CommonJavaSerializer.java @@ -0,0 +1,21 @@ +package org.apache.dubbo.common.serialize.kryo.serializer; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.serializers.JavaSerializer; +import org.apache.dubbo.common.serialize.kryo.ExtensionKryo; + +public class CommonJavaSerializer extends JavaSerializer { + private Kryo kryo = new ExtensionKryo(); + + @Override + public void write(Kryo kryo, Output output, Object object) { + super.write(this.kryo, output, object); + } + + @Override + public Object read(Kryo kryo, Input input, Class type) { + return super.read(this.kryo, input, type); + } +} diff --git a/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/utils/AbstractKryoFactory.java b/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/utils/AbstractKryoFactory.java index ca0abad42fa..f1e947cdc5c 100644 --- a/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/utils/AbstractKryoFactory.java +++ b/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/utils/AbstractKryoFactory.java @@ -16,42 +16,19 @@ */ package org.apache.dubbo.common.serialize.kryo.utils; -import org.apache.dubbo.common.serialize.kryo.CompatibleKryo; -import org.apache.dubbo.common.serialize.support.SerializableClassRegistry; - import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.pool.KryoFactory; import com.esotericsoftware.kryo.serializers.DefaultSerializers; -import de.javakaffee.kryoserializers.ArraysAsListSerializer; -import de.javakaffee.kryoserializers.BitSetSerializer; -import de.javakaffee.kryoserializers.GregorianCalendarSerializer; -import de.javakaffee.kryoserializers.JdkProxySerializer; -import de.javakaffee.kryoserializers.RegexSerializer; -import de.javakaffee.kryoserializers.SynchronizedCollectionsSerializer; -import de.javakaffee.kryoserializers.URISerializer; -import de.javakaffee.kryoserializers.UUIDSerializer; -import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer; +import de.javakaffee.kryoserializers.*; +import org.apache.dubbo.common.serialize.kryo.ExtensionKryo; +import org.apache.dubbo.common.serialize.support.SerializableClassRegistry; import java.lang.reflect.InvocationHandler; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.Set; -import java.util.TreeSet; -import java.util.UUID; -import java.util.Vector; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; @@ -86,7 +63,7 @@ public Kryo create() { kryoCreated = true; } - Kryo kryo = new CompatibleKryo(); + Kryo kryo = new ExtensionKryo(); // TODO // kryo.setReferences(false); diff --git a/dubbo-serialization/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/ExtensionKryoTest.java b/dubbo-serialization/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/ExtensionKryoTest.java new file mode 100644 index 00000000000..1db32c412fb --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/ExtensionKryoTest.java @@ -0,0 +1,44 @@ +package org.apache.dubbo.common.serialize.kryo; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.serializers.DefaultSerializers; +import org.apache.dubbo.common.serialize.kryo.serializer.CommonJavaSerializer; +import org.junit.Test; + +import java.time.LocalDateTime; +import java.util.Date; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class ExtensionKryoTest { + + private Kryo extensionKryo = new ExtensionKryo(); + + @Test + public void getDefaultSerializer(){ + Serializer longSerializer = extensionKryo.getDefaultSerializer(Long.class); + assertEquals(CommonJavaSerializer.class, longSerializer.getClass()); + + Serializer localDateTimeSerializer = extensionKryo.getDefaultSerializer(LocalDateTime.class); + assertEquals(CommonJavaSerializer.class, localDateTimeSerializer.getClass()); + + Serializer dateSerializer = extensionKryo.getDefaultSerializer(Date.class); + assertEquals(DefaultSerializers.DateSerializer.class, dateSerializer.getClass()); + + Serializer byteSerializer = extensionKryo.getDefaultSerializer(Byte.class); + assertEquals(CommonJavaSerializer.class, byteSerializer.getClass()); + + } + + @Test + public void newInstance() { + Long longInstance = extensionKryo.newInstance(Long.class); + LocalDateTime localTimeInstance = extensionKryo.newInstance(LocalDateTime.class); + Date dateInstance = extensionKryo.newInstance(Date.class); + assertNotNull(localTimeInstance); + assertNotNull(dateInstance); + assertNotNull(longInstance); + } +} \ No newline at end of file