This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new e4fe9fcf3 refactor(java): refactor type resolver (#2640)
e4fe9fcf3 is described below

commit e4fe9fcf362e84e25aa46ce6cc93393d024d707b
Author: Shawn Yang <[email protected]>
AuthorDate: Mon Sep 22 11:53:14 2025 +0800

    refactor(java): refactor type resolver (#2640)
    
    ## Why?
    
    <!-- Describe the purpose of this PR. -->
    
    ## What does this PR do?
    
    This pr refactored type resolver, which also fixed #2637
    ## Related issues
    
    Closes #2637
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fory/issues/new/choose) describing the
    need to do so and update the document if necessary.
    
    Delete section if not applicable.
    -->
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    <!--
    When the PR has an impact on performance (if you don't know whether the
    PR will have an impact on performance, you can submit the PR first, and
    if it will have impact on performance, the code reviewer will explain
    it), be sure to attach a benchmark data here.
    
    Delete section if not applicable.
    -->
---
 .../org/apache/fory/AbstractThreadSafeFory.java    |   7 +
 .../src/main/java/org/apache/fory/Fory.java        |   2 +-
 .../main/java/org/apache/fory/ThreadSafeFory.java  |   9 +
 .../java/org/apache/fory/config/ForyBuilder.java   |  10 +-
 .../org/apache/fory/logging/LoggerFactory.java     |   2 +-
 .../main/java/org/apache/fory/meta/ClassSpec.java  |  28 +-
 .../java/org/apache/fory/meta/TypeDefDecoder.java  |   4 +-
 .../java/org/apache/fory/meta/TypeDefEncoder.java  |  19 +-
 .../java/org/apache/fory/pool/ThreadPoolFory.java  |  10 -
 .../org/apache/fory/resolver/AllowListChecker.java |   4 +-
 .../org/apache/fory/resolver/ClassChecker.java     |   1 +
 .../java/org/apache/fory/resolver/ClassInfo.java   |   6 +-
 .../org/apache/fory/resolver/ClassResolver.java    | 442 +-------------------
 .../{ClassChecker.java => TypeChecker.java}        |   6 +-
 .../org/apache/fory/resolver/TypeResolver.java     | 449 ++++++++++++++++++++-
 .../org/apache/fory/resolver/XtypeResolver.java    |  31 +-
 .../fory-core/native-image.properties              |   5 +-
 .../java/org/apache/fory/CrossLanguageTest.java    |  21 +
 .../test/java/org/apache/fory/ForyCopyTest.java    |   2 +-
 .../java/org/apache/fory/ThreadSafeForyTest.java   |   2 +-
 .../apache/fory/resolver/AllowListCheckerTest.java |  10 +-
 21 files changed, 576 insertions(+), 494 deletions(-)

diff --git 
a/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java 
b/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java
index a438ec5cb..9e1f31118 100644
--- a/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java
+++ b/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java
@@ -23,6 +23,7 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 import org.apache.fory.annotation.Internal;
 import org.apache.fory.resolver.ClassChecker;
+import org.apache.fory.resolver.TypeChecker;
 import org.apache.fory.serializer.Serializer;
 import org.apache.fory.serializer.SerializerFactory;
 
@@ -78,10 +79,16 @@ public abstract class AbstractThreadSafeFory implements 
ThreadSafeFory {
   }
 
   @Override
+  @Deprecated
   public void setClassChecker(ClassChecker classChecker) {
     registerCallback(fory -> 
fory.getClassResolver().setClassChecker(classChecker));
   }
 
+  @Override
+  public void setTypeChecker(TypeChecker typeChecker) {
+    registerCallback(fory -> 
fory._getTypeResolver().setTypeChecker(typeChecker));
+  }
+
   @Override
   public void ensureSerializersCompiled() {
     execute(
diff --git a/java/fory-core/src/main/java/org/apache/fory/Fory.java 
b/java/fory-core/src/main/java/org/apache/fory/Fory.java
index cfde3b446..7b6c02920 100644
--- a/java/fory-core/src/main/java/org/apache/fory/Fory.java
+++ b/java/fory-core/src/main/java/org/apache/fory/Fory.java
@@ -1215,7 +1215,7 @@ public final class Fory implements BaseFory {
       if (nextReadRefId >= NOT_NULL_VALUE_FLAG) {
         ClassInfo classInfo;
         if (shareMeta) {
-          classInfo = classResolver.readClassInfoWithMetaShare(buffer, cls);
+          classInfo = classResolver.readSharedClassMeta(buffer, cls);
         } else {
           classInfo = classResolver.getClassInfo(cls);
         }
diff --git a/java/fory-core/src/main/java/org/apache/fory/ThreadSafeFory.java 
b/java/fory-core/src/main/java/org/apache/fory/ThreadSafeFory.java
index c031071a8..8c39f918e 100644
--- a/java/fory-core/src/main/java/org/apache/fory/ThreadSafeFory.java
+++ b/java/fory-core/src/main/java/org/apache/fory/ThreadSafeFory.java
@@ -22,6 +22,7 @@ package org.apache.fory;
 import java.nio.ByteBuffer;
 import java.util.function.Function;
 import org.apache.fory.resolver.ClassChecker;
+import org.apache.fory.resolver.TypeChecker;
 import org.apache.fory.serializer.SerializerFactory;
 import org.apache.fory.util.LoaderBinding;
 
@@ -70,8 +71,16 @@ public interface ThreadSafeFory extends BaseFory {
    *
    * @param classChecker {@link ClassChecker} for classChecker
    */
+  @Deprecated
   void setClassChecker(ClassChecker classChecker);
 
+  /**
+   * Set TypeChecker of serializer for current thread only.
+   *
+   * @param typeChecker {@link TypeChecker} for classChecker
+   */
+  void setTypeChecker(TypeChecker typeChecker);
+
   /**
    * Set tSerializerFactory of serializer for current thread only.
    *
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java 
b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
index c9865a9db..7184b00ea 100644
--- a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
+++ b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
@@ -31,7 +31,6 @@ import org.apache.fory.meta.DeflaterMetaCompressor;
 import org.apache.fory.meta.MetaCompressor;
 import org.apache.fory.pool.ThreadPoolFory;
 import org.apache.fory.reflect.ReflectionUtils;
-import org.apache.fory.resolver.ClassResolver;
 import org.apache.fory.serializer.JavaSerializer;
 import org.apache.fory.serializer.ObjectStreamSerializer;
 import org.apache.fory.serializer.Serializer;
@@ -280,8 +279,9 @@ public final class ForyBuilder {
    * attack if the classes `constructor`/`equals`/`hashCode` method contain 
malicious code. Do not
    * disable class registration if you can't ensure your environment are 
*indeed secure*. We are not
    * responsible for security risks if you disable this option. If you disable 
this option, you can
-   * configure {@link org.apache.fory.resolver.ClassChecker} by {@link
-   * ClassResolver#setClassChecker} to control which classes are allowed being 
serialized.
+   * configure {@link org.apache.fory.resolver.TypeChecker} by {@link
+   * org.apache.fory.resolver.TypeResolver#setTypeChecker} to control which 
classes are allowed
+   * being serialized.
    */
   public ForyBuilder requireClassRegistration(boolean 
requireClassRegistration) {
     this.requireClassRegistration = requireClassRegistration;
@@ -458,8 +458,8 @@ public final class ForyBuilder {
       LOG.warn(
           "Class registration isn't forced, unknown classes can be 
deserialized. "
               + "If the environment isn't secure, please enable class 
registration by "
-              + "`ForyBuilder#requireClassRegistration(true)` or configure 
ClassChecker by "
-              + "`ClassResolver#setClassChecker`");
+              + "`ForyBuilder#requireClassRegistration(true)` or configure 
TypeChecker by "
+              + "`TypeResolver#setTypeChecker`");
     }
     if (GraalvmSupport.IN_GRAALVM_NATIVE_IMAGE && asyncCompilationEnabled) {
       LOG.info("Use sync compilation for graalvm native image since it doesn't 
support JIT.");
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/logging/LoggerFactory.java 
b/java/fory-core/src/main/java/org/apache/fory/logging/LoggerFactory.java
index 0f50c3090..0466b1f74 100644
--- a/java/fory-core/src/main/java/org/apache/fory/logging/LoggerFactory.java
+++ b/java/fory-core/src/main/java/org/apache/fory/logging/LoggerFactory.java
@@ -26,7 +26,7 @@ import org.apache.fory.util.GraalvmSupport;
  */
 public class LoggerFactory {
   private static volatile boolean useSlf4jLogger;
-  private static volatile int logLevel = LogLevel.INFO_LEVEL;
+  private static volatile int logLevel = LogLevel.ERROR_LEVEL;
 
   /** Disable Logger, there will be no log output. */
   public static void disableLogging() {
diff --git a/java/fory-core/src/main/java/org/apache/fory/meta/ClassSpec.java 
b/java/fory-core/src/main/java/org/apache/fory/meta/ClassSpec.java
index 257f1a309..62f82e4cc 100644
--- a/java/fory-core/src/main/java/org/apache/fory/meta/ClassSpec.java
+++ b/java/fory-core/src/main/java/org/apache/fory/meta/ClassSpec.java
@@ -29,19 +29,39 @@ public class ClassSpec {
 
   public final boolean isArray;
   public final int dimension;
+  public final int typeId;
+  public Class<?> type;
 
   public ClassSpec(Class<?> cls) {
-    this.entireClassName = cls.getName();
-    isEnum = cls.isEnum();
-    isArray = cls.isArray();
-    dimension = isArray ? TypeUtils.getArrayDimensions(cls) : 0;
+    this(
+        cls.getName(),
+        cls.isEnum(),
+        cls.isArray(),
+        cls.isArray() ? TypeUtils.getArrayDimensions(cls) : 0,
+        -1);
+    type = cls;
+  }
+
+  public ClassSpec(Class<?> cls, int typeId) {
+    this(
+        cls.getName(),
+        cls.isEnum(),
+        cls.isArray(),
+        cls.isArray() ? TypeUtils.getArrayDimensions(cls) : 0,
+        typeId);
   }
 
   public ClassSpec(String entireClassName, boolean isEnum, boolean isArray, 
int dimension) {
+    this(entireClassName, isEnum, isArray, dimension, -1);
+  }
+
+  public ClassSpec(
+      String entireClassName, boolean isEnum, boolean isArray, int dimension, 
int typeId) {
     this.entireClassName = entireClassName;
     this.isEnum = isEnum;
     this.isArray = isArray;
     this.dimension = dimension;
+    this.typeId = typeId;
   }
 
   @Override
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefDecoder.java 
b/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefDecoder.java
index 88cbceb71..af9c229aa 100644
--- a/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefDecoder.java
+++ b/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefDecoder.java
@@ -66,9 +66,9 @@ class TypeDefDecoder {
       int xtypeId = buffer.readVarUint32Small7();
       ClassInfo userTypeInfo = resolver.getUserTypeInfo(xtypeId);
       if (userTypeInfo == null) {
-        classSpec = new 
ClassSpec(NonexistentClass.NonexistentMetaShared.class);
+        classSpec = new 
ClassSpec(NonexistentClass.NonexistentMetaShared.class, xtypeId);
       } else {
-        classSpec = new ClassSpec(userTypeInfo.getCls());
+        classSpec = new ClassSpec(userTypeInfo.getCls(), xtypeId);
       }
     }
     List<ClassDef.FieldInfo> classFields =
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefEncoder.java 
b/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefEncoder.java
index 3a477a78b..19590e83d 100644
--- a/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefEncoder.java
+++ b/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefEncoder.java
@@ -58,10 +58,17 @@ class TypeDefEncoder {
                 fory.getClassResolver().getFieldDescriptors(type, true),
                 false,
                 Function.identity());
-    List<Field> fields =
-        descriptorGrouper.getSortedDescriptors().stream()
-            .map(Descriptor::getField)
-            .collect(Collectors.toList());
+    ClassInfo classInfo = fory._getTypeResolver().getClassInfo(type);
+    List<Field> fields;
+    int xtypeId = classInfo.getXtypeId();
+    if (Types.isStructType(xtypeId & 0xff)) {
+      fields =
+          descriptorGrouper.getSortedDescriptors().stream()
+              .map(Descriptor::getField)
+              .collect(Collectors.toList());
+    } else {
+      fields = new ArrayList<>();
+    }
     return buildClassDefWithFieldInfos(
         fory.getXtypeResolver(), type, 
buildFieldsInfo(fory.getXtypeResolver(), type, fields));
   }
@@ -93,8 +100,6 @@ class TypeDefEncoder {
   static MemoryBuffer encodeClassDef(
       XtypeResolver resolver, Class<?> type, List<FieldInfo> fields) {
     ClassInfo classInfo = resolver.getClassInfo(type);
-    int xtypeId = (classInfo.getXtypeId()) & 0xff;
-    Preconditions.checkArgument(Types.isStructType(xtypeId), "%s is not a 
struct", type);
     MemoryBuffer buffer = MemoryBuffer.newHeapBuffer(128);
     buffer.writeByte(-1); // placeholder for header, update later
     int currentClassHeader = fields.size();
@@ -126,7 +131,7 @@ class TypeDefEncoder {
       buffer = MemoryBuffer.fromByteArray(compressed);
       buffer.writerIndex(compressed.length);
     }
-    return prependHeader(buffer, isCompressed, true);
+    return prependHeader(buffer, isCompressed, !fields.isEmpty());
   }
 
   static Map<String, FieldInfo> getClassFields(Class<?> type, List<FieldInfo> 
fieldsInfo) {
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/pool/ThreadPoolFory.java 
b/java/fory-core/src/main/java/org/apache/fory/pool/ThreadPoolFory.java
index 6f81066c9..b1bebc1a6 100644
--- a/java/fory-core/src/main/java/org/apache/fory/pool/ThreadPoolFory.java
+++ b/java/fory-core/src/main/java/org/apache/fory/pool/ThreadPoolFory.java
@@ -34,7 +34,6 @@ import org.apache.fory.logging.Logger;
 import org.apache.fory.logging.LoggerFactory;
 import org.apache.fory.memory.MemoryBuffer;
 import org.apache.fory.memory.MemoryUtils;
-import org.apache.fory.resolver.ClassChecker;
 import org.apache.fory.serializer.BufferCallback;
 import org.apache.fory.util.LoaderBinding;
 
@@ -294,15 +293,6 @@ public class ThreadPoolFory extends AbstractThreadSafeFory 
{
     return foryPooledObjectFactory.getClassLoader();
   }
 
-  @Override
-  public void setClassChecker(ClassChecker classChecker) {
-    execute(
-        fory -> {
-          fory.getClassResolver().setClassChecker(classChecker);
-          return null;
-        });
-  }
-
   @Override
   public void clearClassLoader(ClassLoader loader) {
     foryPooledObjectFactory.clearClassLoader(loader);
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java 
b/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java
index 9b280cce5..116e21da9 100644
--- 
a/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java
+++ 
b/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java
@@ -35,7 +35,7 @@ import org.apache.fory.serializer.Serializer;
 
 /** White/black list based class checker. */
 @ThreadSafe
-public class AllowListChecker implements ClassChecker {
+public class AllowListChecker implements TypeChecker {
   private static final Logger LOG = 
LoggerFactory.getLogger(AllowListChecker.class);
 
   public enum CheckLevel {
@@ -80,7 +80,7 @@ public class AllowListChecker implements ClassChecker {
   }
 
   @Override
-  public boolean checkClass(ClassResolver classResolver, String className) {
+  public boolean checkType(TypeResolver resolver, String className) {
     try {
       lock.readLock().lock();
       return check(className);
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassChecker.java 
b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassChecker.java
index 9710bc231..5ac38118f 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassChecker.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassChecker.java
@@ -23,6 +23,7 @@ package org.apache.fory.resolver;
  * Check whether class or objects of class should be serialized. If class 
checker will be invoked by
  * multiple {@link ClassResolver}, class checker should be thread safe.
  */
+@Deprecated
 public interface ClassChecker {
   /**
    * Check whether class should be allowed for serialization.
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassInfo.java 
b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassInfo.java
index ef6b154b0..1d7b8fb97 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassInfo.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassInfo.java
@@ -67,13 +67,13 @@ public class ClassInfo {
     this.xtypeId = xtypeId;
     this.serializer = serializer;
     this.classId = classId;
-    if (cls != null && classId == ClassResolver.NO_CLASS_ID) {
+    if (cls != null && classId == TypeResolver.NO_CLASS_ID) {
       Preconditions.checkArgument(typeNameBytes != null);
     }
   }
 
   ClassInfo(
-      ClassResolver classResolver,
+      TypeResolver classResolver,
       Class<?> cls,
       Serializer<?> serializer,
       short classId,
@@ -142,7 +142,7 @@ public class ClassInfo {
     this.serializer = serializer;
   }
 
-  void setSerializer(ClassResolver resolver, Serializer<?> serializer) {
+  void setSerializer(TypeResolver resolver, Serializer<?> serializer) {
     this.serializer = serializer;
     needToWriteClassDef = serializer != null && 
resolver.needToWriteClassDef(serializer);
   }
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java 
b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
index 6d421f374..67fadd73d 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
@@ -32,8 +32,6 @@ import static 
org.apache.fory.serializer.CodegenSerializer.supportCodegenForJava
 import static org.apache.fory.type.TypeUtils.OBJECT_TYPE;
 import static org.apache.fory.type.TypeUtils.getRawType;
 
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
 import java.io.Externalizable;
 import java.io.IOException;
 import java.io.Serializable;
@@ -67,14 +65,12 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.OptionalInt;
-import java.util.Set;
 import java.util.SortedMap;
 import java.util.TimeZone;
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -87,21 +83,14 @@ import org.apache.fory.Fory;
 import org.apache.fory.ForyCopyable;
 import org.apache.fory.annotation.CodegenInvoke;
 import org.apache.fory.annotation.Internal;
-import org.apache.fory.builder.CodecUtils;
-import org.apache.fory.builder.Generated.GeneratedMetaSharedSerializer;
-import org.apache.fory.builder.Generated.GeneratedObjectSerializer;
 import org.apache.fory.builder.JITContext;
 import org.apache.fory.codegen.CodeGenerator;
 import org.apache.fory.codegen.Expression;
 import org.apache.fory.codegen.Expression.Invoke;
 import org.apache.fory.codegen.Expression.Literal;
-import org.apache.fory.collection.IdentityMap;
 import org.apache.fory.collection.IdentityObjectIntMap;
-import org.apache.fory.collection.LongMap;
-import org.apache.fory.collection.ObjectArray;
 import org.apache.fory.collection.ObjectMap;
 import org.apache.fory.collection.Tuple2;
-import org.apache.fory.config.CompatibleMode;
 import org.apache.fory.config.Language;
 import org.apache.fory.exception.InsecureException;
 import org.apache.fory.logging.Logger;
@@ -112,7 +101,6 @@ import org.apache.fory.meta.ClassDef;
 import org.apache.fory.meta.ClassSpec;
 import org.apache.fory.meta.Encoders;
 import org.apache.fory.meta.MetaString;
-import org.apache.fory.meta.TypeExtMeta;
 import org.apache.fory.reflect.ReflectionUtils;
 import org.apache.fory.reflect.TypeRef;
 import org.apache.fory.serializer.ArraySerializers;
@@ -126,7 +114,6 @@ import org.apache.fory.serializer.JavaSerializer;
 import org.apache.fory.serializer.JdkProxySerializer;
 import org.apache.fory.serializer.LambdaSerializer;
 import org.apache.fory.serializer.LocaleSerializer;
-import org.apache.fory.serializer.MetaSharedSerializer;
 import org.apache.fory.serializer.NoneSerializer;
 import org.apache.fory.serializer.NonexistentClass;
 import org.apache.fory.serializer.NonexistentClass.NonexistentMetaShared;
@@ -163,7 +150,6 @@ import org.apache.fory.type.DescriptorGrouper;
 import org.apache.fory.type.GenericType;
 import org.apache.fory.type.TypeUtils;
 import org.apache.fory.util.GraalvmSupport;
-import org.apache.fory.util.GraalvmSupport.GraalvmSerializerHolder;
 import org.apache.fory.util.Preconditions;
 import org.apache.fory.util.StringUtils;
 import org.apache.fory.util.function.Functions;
@@ -176,10 +162,8 @@ import org.apache.fory.util.function.Functions;
 public class ClassResolver extends TypeResolver {
   private static final Logger LOG = 
LoggerFactory.getLogger(ClassResolver.class);
 
-  // bit 0 unset indicates class is written as an id.
-  public static final byte USE_CLASS_VALUE_FLAG = 0b1;
   // preserve 0 as flag for class id not set in ClassInfo`
-  public static final short NO_CLASS_ID = (short) 0;
+  public static final short NO_CLASS_ID = TypeResolver.NO_CLASS_ID;
   public static final short LAMBDA_STUB_ID = 1;
   public static final short JDK_PROXY_STUB_ID = 2;
   public static final short REPLACE_STUB_ID = 3;
@@ -218,75 +202,26 @@ public class ClassResolver extends TypeResolver {
   public static final short HASHSET_CLASS_ID = (short) 
(PRIMITIVE_DOUBLE_ARRAY_CLASS_ID + 5);
   public static final short CLASS_CLASS_ID = (short) 
(PRIMITIVE_DOUBLE_ARRAY_CLASS_ID + 6);
   public static final short EMPTY_OBJECT_ID = (short) 
(PRIMITIVE_DOUBLE_ARRAY_CLASS_ID + 7);
-  // use a lower load factor to minimize hash collision
-  private static final float foryMapLoadFactor = 0.25f;
-  private static final int estimatedNumRegistered = 150;
-  static final String SET_META__CONTEXT_MSG =
-      "Meta context must be set before serialization, "
-          + "please set meta context by SerializationContext.setMetaContext";
-  static final ClassInfo NIL_CLASS_INFO =
-      new ClassInfo(null, null, null, null, false, null, NO_CLASS_ID, 
NOT_SUPPORT_XLANG);
 
   private final Fory fory;
   XtypeResolver xtypeResolver;
   private ClassInfo[] registeredId2ClassInfo = new ClassInfo[] {};
-
-  // IdentityMap has better lookup performance, when loadFactor is 0.05f, 
performance is better
-  private final IdentityMap<Class<?>, ClassInfo> classInfoMap =
-      new IdentityMap<>(estimatedNumRegistered, foryMapLoadFactor);
   private ClassInfo classInfoCache;
   // Every deserialization for unregistered class will query it, performance 
is important.
   private final ObjectMap<TypeNameBytes, ClassInfo> 
compositeNameBytes2ClassInfo =
       new ObjectMap<>(16, foryMapLoadFactor);
-  private final MetaStringResolver metaStringResolver;
-  private final boolean metaContextShareEnabled;
   private final Map<Class<?>, ClassDef> classDefMap = new HashMap<>();
   private Class<?> currentReadClass;
   // class id of last default registered class.
   private short innerEndClassId;
-  private final ExtRegistry extRegistry;
   private final ShimDispatcher shimDispatcher;
 
-  private static class ExtRegistry {
-    // Here we set it to 1 because `NO_CLASS_ID` is 0 to avoid calculating it 
again in
-    // `register(Class<?> cls)`.
-    private short classIdGenerator = 1;
-    private SerializerFactory serializerFactory;
-    private final IdentityMap<Class<?>, Short> registeredClassIdMap =
-        new IdentityMap<>(estimatedNumRegistered);
-    private final BiMap<String, Class<?>> registeredClasses =
-        HashBiMap.create(estimatedNumRegistered);
-    // cache absClassInfo, support customized serializer for abstract or 
interface.
-    private final IdentityMap<Class<?>, ClassInfo> absClassInfo =
-        new IdentityMap<>(estimatedNumRegistered, foryMapLoadFactor);
-    // avoid potential recursive call for seq codec generation.
-    // ex. A->field1: B, B.field1: A
-    private final Set<Class<?>> getClassCtx = new HashSet<>();
-    private final Map<Class<?>, FieldResolver> fieldResolverMap = new 
HashMap<>();
-    private final LongMap<Tuple2<ClassDef, ClassInfo>> classIdToDef = new 
LongMap<>();
-    private final Map<Class<?>, ClassDef> currentLayerClassDef = new 
HashMap<>();
-    // Tuple2<Class, Class>: Tuple2<From Class, To Class>
-    private final Map<Tuple2<Class<?>, Class<?>>, ClassInfo> 
transformedClassInfo = new HashMap<>();
-    // TODO(chaokunyang) Better to  use soft reference, see ObjectStreamClass.
-    private final ConcurrentHashMap<Tuple2<Class<?>, Boolean>, 
SortedMap<Member, Descriptor>>
-        descriptorsCache = new ConcurrentHashMap<>();
-    private ClassChecker classChecker = (classResolver, className) -> true;
-    private GenericType objectGenericType;
-    private final IdentityMap<Type, GenericType> genericTypes = new 
IdentityMap<>();
-    private final Map<Class, Map<String, GenericType>> classGenericTypes = new 
HashMap<>();
-    private final Map<List<ClassLoader>, CodeGenerator> codeGeneratorMap = new 
HashMap<>();
-    private final Set<ClassInfo> initialClassInfos = new HashSet<>();
-  }
-
   public ClassResolver(Fory fory) {
     super(fory);
     this.fory = fory;
-    metaStringResolver = fory.getMetaStringResolver();
     classInfoCache = NIL_CLASS_INFO;
-    metaContextShareEnabled = fory.getConfig().isMetaShareEnabled();
-    extRegistry = new ExtRegistry();
     shimDispatcher = new ShimDispatcher(fory);
-    ClassResolver._addGraalvmClassRegistry(fory.getConfig().getConfigHash(), 
this);
+    _addGraalvmClassRegistry(fory.getConfig().getConfigHash(), this);
   }
 
   @Override
@@ -820,7 +755,7 @@ public class ClassResolver extends TypeResolver {
 
   /**
    * Set serializer to avoid circular error when there is a serializer query 
for fields by {@link
-   * #readClassInfoWithMetaShare} and {@link #getSerializer(Class)} which 
access current creating
+   * #readSharedClassMeta} and {@link #getSerializer(Class)} which access 
current creating
    * serializer. This method is used to avoid overwriting existing serializer 
for class when
    * creating a data serializer for serialization of parts fields of a class.
    */
@@ -1129,12 +1064,15 @@ public class ClassResolver extends TypeResolver {
     }
   }
 
-  public ClassChecker getClassChecker() {
-    return extRegistry.classChecker;
-  }
-
+  @Deprecated
   public void setClassChecker(ClassChecker classChecker) {
-    extRegistry.classChecker = classChecker;
+    extRegistry.typeChecker =
+        (resolver, className) -> {
+          if (resolver instanceof ClassResolver) {
+            return classChecker.checkClass((ClassResolver) resolver, 
className);
+          }
+          return false;
+        };
   }
 
   public FieldResolver getFieldResolver(Class<?> cls) {
@@ -1169,30 +1107,6 @@ public class ClassResolver extends TypeResolver {
         Tuple2.of(clz, searchParent), t -> 
Descriptor.getAllDescriptorsMap(clz, searchParent));
   }
 
-  /**
-   * Whether to track reference for this type. If false, reference tracing of 
subclasses may be
-   * ignored too.
-   */
-  @Override
-  public boolean needToWriteRef(TypeRef<?> typeRef) {
-    Object extInfo = typeRef.getExtInfo();
-    if (extInfo instanceof TypeExtMeta) {
-      TypeExtMeta meta = (TypeExtMeta) extInfo;
-      return meta.trackingRef();
-    }
-    Class<?> cls = typeRef.getRawType();
-    if (fory.trackingRef()) {
-      ClassInfo classInfo = getClassInfo(cls, false);
-      if (classInfo == null || classInfo.serializer == null) {
-        // TODO group related logic together for extendability and consistency.
-        return !cls.isEnum();
-      } else {
-        return classInfo.serializer.needToWriteRef();
-      }
-    }
-    return false;
-  }
-
   public ClassInfo getClassInfo(short classId) {
     ClassInfo classInfo = registeredId2ClassInfo[classId];
     assert classInfo != null : classId;
@@ -1403,7 +1317,7 @@ public class ClassResolver extends TypeResolver {
           || extRegistry.registeredClassIdMap.containsKey(cls)
           || shimDispatcher.contains(cls);
     } else {
-      return extRegistry.classChecker.checkClass(this, cls.getName());
+      return extRegistry.typeChecker.checkType(this, cls.getName());
     }
     // Don't take java Exception as secure in case future JDK introduce 
insecure JDK exception.
     // if (Exception.class.isAssignableFrom(cls)
@@ -1502,25 +1416,8 @@ public class ClassResolver extends TypeResolver {
     return classDef;
   }
 
-  boolean needToWriteClassDef(Serializer serializer) {
-    if (fory.getConfig().getCompatibleMode() != CompatibleMode.COMPATIBLE) {
-      return false;
-    }
-    if (GraalvmSupport.isGraalBuildtime() && serializer instanceof 
GraalvmSerializerHolder) {
-      Class<? extends Serializer> serializerClass =
-          ((GraalvmSerializerHolder) serializer).getSerializerClass();
-      return GeneratedObjectSerializer.class.isAssignableFrom(serializerClass)
-          || 
GeneratedMetaSharedSerializer.class.isAssignableFrom(serializerClass);
-    }
-    return (serializer instanceof GeneratedObjectSerializer
-        // May already switched to MetaSharedSerializer when update class info 
cache.
-        || serializer instanceof GeneratedMetaSharedSerializer
-        || serializer instanceof LazyInitBeanSerializer
-        || serializer instanceof ObjectSerializer
-        || serializer instanceof MetaSharedSerializer);
-  }
-
-  private ClassInfo readClassInfoWithMetaShare(MemoryBuffer buffer, 
MetaContext metaContext) {
+  @Override
+  public ClassInfo readSharedClassMeta(MemoryBuffer buffer, MetaContext 
metaContext) {
     assert metaContext != null : SET_META__CONTEXT_MSG;
     int header = buffer.readVarUint32Small14();
     int id = header >>> 1;
@@ -1529,181 +1426,11 @@ public class ClassResolver extends TypeResolver {
     }
     ClassInfo classInfo = metaContext.readClassInfos.get(id);
     if (classInfo == null) {
-      classInfo = readClassInfoWithMetaShare(metaContext, id);
-    }
-    return classInfo;
-  }
-
-  ClassInfo readClassInfoWithMetaShare(MetaContext metaContext, int index) {
-    ClassDef classDef = metaContext.readClassDefs.get(index);
-    Tuple2<ClassDef, ClassInfo> classDefTuple = 
extRegistry.classIdToDef.get(classDef.getId());
-    ClassInfo classInfo;
-    if (classDefTuple == null || classDefTuple.f1 == null || 
classDefTuple.f1.serializer == null) {
-      classInfo = buildMetaSharedClassInfo(classDefTuple, classDef);
-    } else {
-      classInfo = classDefTuple.f1;
+      classInfo = readSharedClassMeta(metaContext, id);
     }
-    metaContext.readClassInfos.set(index, classInfo);
     return classInfo;
   }
 
-  public ClassInfo readClassInfoWithMetaShare(MemoryBuffer buffer, Class<?> 
targetClass) {
-    assert metaContextShareEnabled;
-    ClassInfo classInfo =
-        readClassInfoWithMetaShare(buffer, 
fory.getSerializationContext().getMetaContext());
-    Class<?> readClass = classInfo.getCls();
-    // replace target class if needed
-    if (targetClass != readClass) {
-      Tuple2<Class<?>, Class<?>> key = Tuple2.of(readClass, targetClass);
-      ClassInfo newClassInfo = extRegistry.transformedClassInfo.get(key);
-      if (newClassInfo == null) {
-        // similar to create serializer for `NonexistentMetaShared`
-        newClassInfo =
-            getMetaSharedClassInfo(
-                classInfo.classDef.replaceRootClassTo(this, targetClass), 
targetClass);
-        extRegistry.transformedClassInfo.put(key, newClassInfo);
-      }
-      return newClassInfo;
-    }
-    return classInfo;
-  }
-
-  private ClassInfo buildMetaSharedClassInfo(
-      Tuple2<ClassDef, ClassInfo> classDefTuple, ClassDef classDef) {
-    ClassInfo classInfo;
-    if (classDefTuple != null) {
-      classDef = classDefTuple.f0;
-    }
-    Class<?> cls = loadClass(classDef.getClassSpec());
-    if (!classDef.hasFieldsMeta()) {
-      classInfo = getClassInfo(cls);
-    } else {
-      classInfo = getMetaSharedClassInfo(classDef, cls);
-    }
-    // Share serializer for same version class def to avoid too much different 
meta
-    // context take up too much memory.
-    putClassDef(classDef, classInfo);
-    return classInfo;
-  }
-
-  // TODO(chaokunyang) if ClassDef is consistent with class in this process,
-  //  use existing serializer instead.
-  private ClassInfo getMetaSharedClassInfo(ClassDef classDef, Class<?> clz) {
-    if (clz == NonexistentSkip.class) {
-      clz = NonexistentMetaShared.class;
-    }
-    Class<?> cls = clz;
-    Short classId = extRegistry.registeredClassIdMap.get(cls);
-    ClassInfo classInfo =
-        new ClassInfo(this, cls, null, classId == null ? NO_CLASS_ID : 
classId, NOT_SUPPORT_XLANG);
-    classInfo.classDef = classDef;
-    if 
(NonexistentClass.class.isAssignableFrom(TypeUtils.getComponentIfArray(cls))) {
-      if (cls == NonexistentMetaShared.class) {
-        classInfo.setSerializer(this, new NonexistentClassSerializer(fory, 
classDef));
-        // ensure `NonexistentMetaSharedClass` registered to write 
fixed-length class def,
-        // so we can rewrite it in `NonexistentClassSerializer`.
-        Preconditions.checkNotNull(classId);
-      } else {
-        classInfo.serializer =
-            NonexistentClassSerializers.getSerializer(fory, 
classDef.getClassName(), cls);
-      }
-      return classInfo;
-    }
-    if (clz.isArray() || cls.isEnum()) {
-      return getClassInfo(cls);
-    }
-    Class<? extends Serializer> sc =
-        getMetaSharedDeserializerClassFromGraalvmRegistry(cls, classDef);
-    if (sc == null) {
-      if (GraalvmSupport.isGraalRuntime()) {
-        sc = MetaSharedSerializer.class;
-        LOG.warn(
-            "Can't generate class at runtime in graalvm for class def {}, use 
{} instead",
-            classDef,
-            sc);
-      } else {
-        sc =
-            fory.getJITContext()
-                .registerSerializerJITCallback(
-                    () -> MetaSharedSerializer.class,
-                    () -> CodecUtils.loadOrGenMetaSharedCodecClass(fory, cls, 
classDef),
-                    c -> classInfo.setSerializer(this, 
Serializers.newSerializer(fory, cls, c)));
-      }
-    }
-    if (sc == MetaSharedSerializer.class) {
-      classInfo.setSerializer(this, new MetaSharedSerializer(fory, cls, 
classDef));
-    } else {
-      classInfo.setSerializer(this, Serializers.newSerializer(fory, cls, sc));
-    }
-    return classInfo;
-  }
-
-  /**
-   * Write all new class definitions meta to buffer at last, so that if some 
class doesn't exist on
-   * peer, but one of class which exists on both side are sent in this stream, 
the definition meta
-   * can still be stored in peer, and can be resolved next time when sent only 
an id.
-   */
-  public void writeClassDefs(MemoryBuffer buffer) {
-    MetaContext metaContext = fory.getSerializationContext().getMetaContext();
-    ObjectArray<ClassDef> writingClassDefs = metaContext.writingClassDefs;
-    final int size = writingClassDefs.size;
-    buffer.writeVarUint32Small7(size);
-    if (buffer.isHeapFullyWriteable()) {
-      writeClassDefs(buffer, writingClassDefs, size);
-    } else {
-      for (int i = 0; i < size; i++) {
-        writingClassDefs.get(i).writeClassDef(buffer);
-      }
-    }
-    metaContext.writingClassDefs.size = 0;
-  }
-
-  private void writeClassDefs(
-      MemoryBuffer buffer, ObjectArray<ClassDef> writingClassDefs, int size) {
-    for (int i = 0; i < size; i++) {
-      buffer.writeBytes(writingClassDefs.get(i).getEncoded());
-      MemoryBuffer memoryBuffer = 
MemoryBuffer.fromByteArray(writingClassDefs.get(i).getEncoded());
-      ClassDef.readClassDef(fory, memoryBuffer, memoryBuffer.readInt64());
-    }
-  }
-
-  /**
-   * Ensure all class definition are read and populated, even there are 
deserialization exception
-   * such as ClassNotFound. So next time a class def written previously 
identified by an id can be
-   * got from the meta context.
-   */
-  public void readClassDefs(MemoryBuffer buffer) {
-    MetaContext metaContext = fory.getSerializationContext().getMetaContext();
-    assert metaContext != null : SET_META__CONTEXT_MSG;
-    int numClassDefs = buffer.readVarUint32Small7();
-    for (int i = 0; i < numClassDefs; i++) {
-      long id = buffer.readInt64();
-      Tuple2<ClassDef, ClassInfo> tuple2 = extRegistry.classIdToDef.get(id);
-      if (tuple2 != null) {
-        ClassDef.skipClassDef(buffer, id);
-      } else {
-        tuple2 = readClassDef(buffer, id);
-      }
-      metaContext.readClassDefs.add(tuple2.f0);
-      metaContext.readClassInfos.add(tuple2.f1);
-    }
-  }
-
-  private Tuple2<ClassDef, ClassInfo> readClassDef(MemoryBuffer buffer, long 
header) {
-    ClassDef readClassDef = ClassDef.readClassDef(fory, buffer, header);
-    Tuple2<ClassDef, ClassInfo> tuple2 = 
extRegistry.classIdToDef.get(readClassDef.getId());
-    if (tuple2 == null) {
-      tuple2 = putClassDef(readClassDef, null);
-    }
-    return tuple2;
-  }
-
-  private Tuple2<ClassDef, ClassInfo> putClassDef(ClassDef classDef, ClassInfo 
classInfo) {
-    Tuple2<ClassDef, ClassInfo> tuple2 = Tuple2.of(classDef, classInfo);
-    extRegistry.classIdToDef.put(classDef.getId(), tuple2);
-    return tuple2;
-  }
-
   @Override
   public ClassDef getTypeDef(Class<?> cls, boolean resolveParent) {
     if (resolveParent) {
@@ -1798,7 +1525,7 @@ public class ClassResolver extends TypeResolver {
    */
   public ClassInfo readClassInfo(MemoryBuffer buffer) {
     if (metaContextShareEnabled) {
-      return readClassInfoWithMetaShare(buffer, 
fory.getSerializationContext().getMetaContext());
+      return readSharedClassMeta(buffer, 
fory.getSerializationContext().getMetaContext());
     }
     int header = buffer.readVarUint32Small14();
     ClassInfo classInfo;
@@ -1820,7 +1547,7 @@ public class ClassResolver extends TypeResolver {
   @Override
   public ClassInfo readClassInfo(MemoryBuffer buffer, ClassInfo 
classInfoCache) {
     if (metaContextShareEnabled) {
-      return readClassInfoWithMetaShare(buffer, 
fory.getSerializationContext().getMetaContext());
+      return readSharedClassMeta(buffer, 
fory.getSerializationContext().getMetaContext());
     }
     int header = buffer.readVarUint32Small14();
     if ((header & 0b1) != 0) {
@@ -1835,7 +1562,7 @@ public class ClassResolver extends TypeResolver {
   @CodegenInvoke
   public ClassInfo readClassInfo(MemoryBuffer buffer, ClassInfoHolder 
classInfoHolder) {
     if (metaContextShareEnabled) {
-      return readClassInfoWithMetaShare(buffer, 
fory.getSerializationContext().getMetaContext());
+      return readSharedClassMeta(buffer, 
fory.getSerializationContext().getMetaContext());
     }
     int header = buffer.readVarUint32Small14();
     if ((header & 0b1) != 0) {
@@ -1848,7 +1575,7 @@ public class ClassResolver extends TypeResolver {
   private ClassInfo readClassInfoByCache(
       MemoryBuffer buffer, ClassInfo classInfoCache, int header) {
     if (metaContextShareEnabled) {
-      return readClassInfoWithMetaShare(buffer, 
fory.getSerializationContext().getMetaContext());
+      return readSharedClassMeta(buffer, 
fory.getSerializationContext().getMetaContext());
     }
     return readClassInfoFromBytes(buffer, classInfoCache, header);
   }
@@ -1856,7 +1583,7 @@ public class ClassResolver extends TypeResolver {
   private ClassInfo readClassInfoFromBytes(
       MemoryBuffer buffer, ClassInfoHolder classInfoHolder, int header) {
     if (metaContextShareEnabled) {
-      return readClassInfoWithMetaShare(buffer, 
fory.getSerializationContext().getMetaContext());
+      return readSharedClassMeta(buffer, 
fory.getSerializationContext().getMetaContext());
     }
     ClassInfo classInfo = readClassInfoFromBytes(buffer, 
classInfoHolder.classInfo, header);
     classInfoHolder.classInfo = classInfo;
@@ -1939,41 +1666,6 @@ public class ClassResolver extends TypeResolver {
     return currentReadClass;
   }
 
-  private Class<?> loadClass(ClassSpec classSpec) {
-    return loadClass(classSpec.entireClassName, classSpec.isEnum, 
classSpec.dimension);
-  }
-
-  private Class<?> loadClass(String className, boolean isEnum, int arrayDims) {
-    return loadClass(className, isEnum, arrayDims, 
fory.getConfig().deserializeNonexistentClass());
-  }
-
-  private Class<?> loadClass(
-      String className, boolean isEnum, int arrayDims, boolean 
deserializeNonexistentClass) {
-    extRegistry.classChecker.checkClass(this, className);
-    Class<?> cls = extRegistry.registeredClasses.get(className);
-    if (cls != null) {
-      return cls;
-    }
-    try {
-      return Class.forName(className, false, fory.getClassLoader());
-    } catch (ClassNotFoundException e) {
-      try {
-        return Class.forName(className, false, 
Thread.currentThread().getContextClassLoader());
-      } catch (ClassNotFoundException ex) {
-        String msg =
-            String.format(
-                "Class %s not found from classloaders [%s, %s]",
-                className, fory.getClassLoader(), 
Thread.currentThread().getContextClassLoader());
-        if (deserializeNonexistentClass) {
-          LOG.warn(msg);
-          return NonexistentClass.getNonexistentClass(
-              className, isEnum, arrayDims, metaContextShareEnabled);
-        }
-        throw new IllegalStateException(msg, ex);
-      }
-    }
-  }
-
   public void reset() {
     resetRead();
     resetWrite();
@@ -2052,10 +1744,6 @@ public class ClassResolver extends TypeResolver {
     return classId >= PRIMITIVE_VOID_CLASS_ID && classId <= 
PRIMITIVE_DOUBLE_CLASS_ID;
   }
 
-  public MetaStringResolver getMetaStringResolver() {
-    return metaStringResolver;
-  }
-
   public CodeGenerator getCodeGenerator(ClassLoader... loaders) {
     List<ClassLoader> loaderList = new ArrayList<>(loaders.length);
     Collections.addAll(loaderList, loaders);
@@ -2115,94 +1803,4 @@ public class ClassResolver extends TypeResolver {
       fory.getJITContext().unlock();
     }
   }
-
-  private static final ConcurrentMap<Integer, GraalvmClassRegistry> 
GRAALVM_REGISTRY =
-      new ConcurrentHashMap<>();
-
-  // CHECKSTYLE.OFF:MethodName
-  public static void _addGraalvmClassRegistry(int foryConfigHash, 
ClassResolver classResolver) {
-    // CHECKSTYLE.ON:MethodName
-    if (GraalvmSupport.isGraalBuildtime()) {
-      GraalvmClassRegistry registry =
-          GRAALVM_REGISTRY.computeIfAbsent(foryConfigHash, k -> new 
GraalvmClassRegistry());
-      registry.resolvers.add(classResolver);
-    }
-  }
-
-  private static class GraalvmClassRegistry {
-    private final List<ClassResolver> resolvers;
-    private final Map<Class<?>, Class<? extends Serializer>> 
serializerClassMap;
-    private final Map<Long, Class<? extends Serializer>> deserializerClassMap;
-
-    private GraalvmClassRegistry() {
-      resolvers = Collections.synchronizedList(new ArrayList<>());
-      serializerClassMap = new ConcurrentHashMap<>();
-      deserializerClassMap = new ConcurrentHashMap<>();
-    }
-  }
-
-  private GraalvmClassRegistry getGraalvmClassRegistry() {
-    return GRAALVM_REGISTRY.computeIfAbsent(
-        fory.getConfig().getConfigHash(), k -> new GraalvmClassRegistry());
-  }
-
-  private Class<? extends Serializer> getGraalvmSerializerClass(Serializer 
serializer) {
-    if (serializer instanceof GraalvmSerializerHolder) {
-      return ((GraalvmSerializerHolder) serializer).getSerializerClass();
-    }
-    return serializer.getClass();
-  }
-
-  private Class<? extends Serializer> 
getSerializerClassFromGraalvmRegistry(Class<?> cls) {
-    GraalvmClassRegistry registry = getGraalvmClassRegistry();
-    List<ClassResolver> classResolvers = registry.resolvers;
-    if (classResolvers.isEmpty()) {
-      return null;
-    }
-    for (ClassResolver classResolver : classResolvers) {
-      if (classResolver != this) {
-        ClassInfo classInfo = classResolver.classInfoMap.get(cls);
-        if (classInfo != null && classInfo.serializer != null) {
-          return classInfo.serializer.getClass();
-        }
-      }
-    }
-    Class<? extends Serializer> serializerClass = 
registry.serializerClassMap.get(cls);
-    // noinspection Duplicates
-    if (serializerClass != null) {
-      return serializerClass;
-    }
-    if (GraalvmSupport.isGraalRuntime()) {
-      if (Functions.isLambda(cls) || ReflectionUtils.isJdkProxy(cls)) {
-        return null;
-      }
-      throw new RuntimeException(String.format("Class %s is not registered", 
cls));
-    }
-    return null;
-  }
-
-  private Class<? extends Serializer> 
getMetaSharedDeserializerClassFromGraalvmRegistry(
-      Class<?> cls, ClassDef classDef) {
-    GraalvmClassRegistry registry = getGraalvmClassRegistry();
-    List<ClassResolver> classResolvers = registry.resolvers;
-    if (classResolvers.isEmpty()) {
-      return null;
-    }
-    Class<? extends Serializer> deserializerClass =
-        registry.deserializerClassMap.get(classDef.getId());
-    // noinspection Duplicates
-    if (deserializerClass != null) {
-      return deserializerClass;
-    }
-    if (GraalvmSupport.isGraalRuntime()) {
-      if (Functions.isLambda(cls) || ReflectionUtils.isJdkProxy(cls)) {
-        return null;
-      }
-      throw new RuntimeException(
-          String.format(
-              "Class %s is not registered, registered classes: %s",
-              cls, registry.deserializerClassMap));
-    }
-    return null;
-  }
 }
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassChecker.java 
b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeChecker.java
similarity index 89%
copy from 
java/fory-core/src/main/java/org/apache/fory/resolver/ClassChecker.java
copy to java/fory-core/src/main/java/org/apache/fory/resolver/TypeChecker.java
index 9710bc231..3b12d96cb 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassChecker.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeChecker.java
@@ -23,13 +23,13 @@ package org.apache.fory.resolver;
  * Check whether class or objects of class should be serialized. If class 
checker will be invoked by
  * multiple {@link ClassResolver}, class checker should be thread safe.
  */
-public interface ClassChecker {
+public interface TypeChecker {
   /**
    * Check whether class should be allowed for serialization.
    *
-   * @param classResolver class resolver
+   * @param resolver type resolver
    * @param className full name of class
    * @return true if class is allowed for serialization.
    */
-  boolean checkClass(ClassResolver classResolver, String className);
+  boolean checkType(TypeResolver resolver, String className);
 }
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java 
b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
index 59bef735f..7c5bf9985 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
@@ -19,41 +19,138 @@
 
 package org.apache.fory.resolver;
 
+import static org.apache.fory.Fory.NOT_SUPPORT_XLANG;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
 import java.lang.reflect.Field;
+import java.lang.reflect.Member;
 import java.lang.reflect.Type;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
 import org.apache.fory.Fory;
 import org.apache.fory.annotation.Internal;
+import org.apache.fory.builder.CodecUtils;
+import org.apache.fory.builder.Generated.GeneratedMetaSharedSerializer;
+import org.apache.fory.builder.Generated.GeneratedObjectSerializer;
+import org.apache.fory.codegen.CodeGenerator;
 import org.apache.fory.codegen.Expression;
 import org.apache.fory.codegen.Expression.Invoke;
+import org.apache.fory.collection.IdentityMap;
+import org.apache.fory.collection.LongMap;
+import org.apache.fory.collection.ObjectArray;
 import org.apache.fory.collection.Tuple2;
+import org.apache.fory.config.CompatibleMode;
+import org.apache.fory.logging.Logger;
+import org.apache.fory.logging.LoggerFactory;
 import org.apache.fory.memory.MemoryBuffer;
 import org.apache.fory.meta.ClassDef;
+import org.apache.fory.meta.ClassSpec;
+import org.apache.fory.meta.TypeExtMeta;
 import org.apache.fory.reflect.ReflectionUtils;
 import org.apache.fory.reflect.TypeRef;
+import org.apache.fory.serializer.CodegenSerializer.LazyInitBeanSerializer;
+import org.apache.fory.serializer.MetaSharedSerializer;
+import org.apache.fory.serializer.NonexistentClass;
 import org.apache.fory.serializer.NonexistentClass.NonexistentMetaShared;
+import org.apache.fory.serializer.NonexistentClass.NonexistentSkip;
+import org.apache.fory.serializer.NonexistentClassSerializers;
+import 
org.apache.fory.serializer.NonexistentClassSerializers.NonexistentClassSerializer;
+import org.apache.fory.serializer.ObjectSerializer;
 import org.apache.fory.serializer.Serializer;
+import org.apache.fory.serializer.SerializerFactory;
+import org.apache.fory.serializer.Serializers;
 import org.apache.fory.type.Descriptor;
 import org.apache.fory.type.DescriptorGrouper;
 import org.apache.fory.type.GenericType;
 import org.apache.fory.type.ScalaTypes;
 import org.apache.fory.type.TypeUtils;
+import org.apache.fory.util.GraalvmSupport;
+import org.apache.fory.util.GraalvmSupport.GraalvmSerializerHolder;
+import org.apache.fory.util.Preconditions;
+import org.apache.fory.util.function.Functions;
 
 // Internal type dispatcher.
 // Do not use this interface outside of fory package
 @Internal
+@SuppressWarnings({"rawtypes", "unchecked"})
 public abstract class TypeResolver {
-  private final Fory fory;
+  private static final Logger LOG = 
LoggerFactory.getLogger(ClassResolver.class);
+
+  public static final short NO_CLASS_ID = (short) 0;
+  static final ClassInfo NIL_CLASS_INFO =
+      new ClassInfo(null, null, null, null, false, null, NO_CLASS_ID, 
NOT_SUPPORT_XLANG);
+  // use a lower load factor to minimize hash collision
+  static final float foryMapLoadFactor = 0.25f;
+  static final int estimatedNumRegistered = 150;
+  static final String SET_META__CONTEXT_MSG =
+      "Meta context must be set before serialization, "
+          + "please set meta context by SerializationContext.setMetaContext";
+
+  final Fory fory;
+  final boolean metaContextShareEnabled;
+  final MetaStringResolver metaStringResolver;
+  // IdentityMap has better lookup performance, when loadFactor is 0.05f, 
performance is better
+  final IdentityMap<Class<?>, ClassInfo> classInfoMap = new IdentityMap<>(64, 
foryMapLoadFactor);
+  final ExtRegistry extRegistry;
 
   protected TypeResolver(Fory fory) {
     this.fory = fory;
+    metaContextShareEnabled = fory.getConfig().isMetaShareEnabled();
+    extRegistry = new ExtRegistry();
+    metaStringResolver = fory.getMetaStringResolver();
   }
 
-  public abstract boolean needToWriteRef(TypeRef<?> typeRef);
+  /**
+   * Whether to track reference for this type. If false, reference tracing of 
subclasses may be
+   * ignored too.
+   */
+  public final boolean needToWriteRef(TypeRef<?> typeRef) {
+    Object extInfo = typeRef.getExtInfo();
+    if (extInfo instanceof TypeExtMeta) {
+      TypeExtMeta meta = (TypeExtMeta) extInfo;
+      return meta.trackingRef();
+    }
+    Class<?> cls = typeRef.getRawType();
+    if (fory.trackingRef()) {
+      ClassInfo classInfo = classInfoMap.get(cls);
+      if (classInfo == null || classInfo.serializer == null) {
+        // TODO group related logic together for extendability and consistency.
+        return !cls.isEnum();
+      } else {
+        return classInfo.serializer.needToWriteRef();
+      }
+    }
+    return false;
+  }
+
+  public final boolean needToWriteClassDef(Serializer serializer) {
+    if (fory.getConfig().getCompatibleMode() != CompatibleMode.COMPATIBLE) {
+      return false;
+    }
+    if (GraalvmSupport.isGraalBuildtime() && serializer instanceof 
GraalvmSerializerHolder) {
+      Class<? extends Serializer> serializerClass =
+          ((GraalvmSerializerHolder) serializer).getSerializerClass();
+      return GeneratedObjectSerializer.class.isAssignableFrom(serializerClass)
+          || 
GeneratedMetaSharedSerializer.class.isAssignableFrom(serializerClass);
+    }
+    return (serializer instanceof GeneratedObjectSerializer
+        // May already switched to MetaSharedSerializer when update class info 
cache.
+        || serializer instanceof GeneratedMetaSharedSerializer
+        || serializer instanceof LazyInitBeanSerializer
+        || serializer instanceof ObjectSerializer
+        || serializer instanceof MetaSharedSerializer);
+  }
 
   public abstract boolean isRegistered(Class<?> cls);
 
@@ -87,6 +184,216 @@ public abstract class TypeResolver {
 
   public abstract ClassInfo readClassInfo(MemoryBuffer buffer, ClassInfo 
classInfoCache);
 
+  abstract ClassInfo readSharedClassMeta(MemoryBuffer buffer, MetaContext 
metaContext);
+
+  public final ClassInfo readSharedClassMeta(MemoryBuffer buffer, Class<?> 
targetClass) {
+    ClassInfo classInfo =
+        readSharedClassMeta(buffer, 
fory.getSerializationContext().getMetaContext());
+    Class<?> readClass = classInfo.getCls();
+    // replace target class if needed
+    if (targetClass != readClass) {
+      Tuple2<Class<?>, Class<?>> key = Tuple2.of(readClass, targetClass);
+      ClassInfo newClassInfo = extRegistry.transformedClassInfo.get(key);
+      if (newClassInfo == null) {
+        // similar to create serializer for `NonexistentMetaShared`
+        newClassInfo =
+            getMetaSharedClassInfo(
+                classInfo.classDef.replaceRootClassTo((ClassResolver) this, 
targetClass),
+                targetClass);
+        extRegistry.transformedClassInfo.put(key, newClassInfo);
+      }
+      return newClassInfo;
+    }
+    return classInfo;
+  }
+
+  final ClassInfo readSharedClassMeta(MetaContext metaContext, int index) {
+    ClassDef classDef = metaContext.readClassDefs.get(index);
+    Tuple2<ClassDef, ClassInfo> classDefTuple = 
extRegistry.classIdToDef.get(classDef.getId());
+    ClassInfo classInfo;
+    if (classDefTuple == null || classDefTuple.f1 == null || 
classDefTuple.f1.serializer == null) {
+      classInfo = buildMetaSharedClassInfo(classDefTuple, classDef);
+    } else {
+      classInfo = classDefTuple.f1;
+    }
+    metaContext.readClassInfos.set(index, classInfo);
+    return classInfo;
+  }
+
+  final ClassInfo buildMetaSharedClassInfo(
+      Tuple2<ClassDef, ClassInfo> classDefTuple, ClassDef classDef) {
+    ClassInfo classInfo;
+    if (classDefTuple != null) {
+      classDef = classDefTuple.f0;
+    }
+    Class<?> cls = loadClass(classDef.getClassSpec());
+    if (!classDef.hasFieldsMeta()) {
+      classInfo = getClassInfo(cls);
+    } else {
+      classInfo = getMetaSharedClassInfo(classDef, cls);
+    }
+    // Share serializer for same version class def to avoid too much different 
meta
+    // context take up too much memory.
+    putClassDef(classDef, classInfo);
+    return classInfo;
+  }
+
+  // TODO(chaokunyang) if ClassDef is consistent with class in this process,
+  //  use existing serializer instead.
+  private ClassInfo getMetaSharedClassInfo(ClassDef classDef, Class<?> clz) {
+    if (clz == NonexistentSkip.class) {
+      clz = NonexistentMetaShared.class;
+    }
+    Class<?> cls = clz;
+    Short classId = extRegistry.registeredClassIdMap.get(cls);
+    ClassInfo classInfo =
+        new ClassInfo(this, cls, null, classId == null ? NO_CLASS_ID : 
classId, NOT_SUPPORT_XLANG);
+    classInfo.classDef = classDef;
+    if 
(NonexistentClass.class.isAssignableFrom(TypeUtils.getComponentIfArray(cls))) {
+      if (cls == NonexistentMetaShared.class) {
+        classInfo.setSerializer(this, new NonexistentClassSerializer(fory, 
classDef));
+        // ensure `NonexistentMetaSharedClass` registered to write 
fixed-length class def,
+        // so we can rewrite it in `NonexistentClassSerializer`.
+        Preconditions.checkNotNull(classId);
+      } else {
+        classInfo.serializer =
+            NonexistentClassSerializers.getSerializer(fory, 
classDef.getClassName(), cls);
+      }
+      return classInfo;
+    }
+    if (clz.isArray() || cls.isEnum()) {
+      return getClassInfo(cls);
+    }
+    Class<? extends Serializer> sc =
+        getMetaSharedDeserializerClassFromGraalvmRegistry(cls, classDef);
+    if (sc == null) {
+      if (GraalvmSupport.isGraalRuntime()) {
+        sc = MetaSharedSerializer.class;
+        LOG.warn(
+            "Can't generate class at runtime in graalvm for class def {}, use 
{} instead",
+            classDef,
+            sc);
+      } else {
+        sc =
+            fory.getJITContext()
+                .registerSerializerJITCallback(
+                    () -> MetaSharedSerializer.class,
+                    () -> CodecUtils.loadOrGenMetaSharedCodecClass(fory, cls, 
classDef),
+                    c -> classInfo.setSerializer(this, 
Serializers.newSerializer(fory, cls, c)));
+      }
+    }
+    if (sc == MetaSharedSerializer.class) {
+      classInfo.setSerializer(this, new MetaSharedSerializer(fory, cls, 
classDef));
+    } else {
+      classInfo.setSerializer(this, Serializers.newSerializer(fory, cls, sc));
+    }
+    return classInfo;
+  }
+
+  /**
+   * Write all new class definitions meta to buffer at last, so that if some 
class doesn't exist on
+   * peer, but one of class which exists on both side are sent in this stream, 
the definition meta
+   * can still be stored in peer, and can be resolved next time when sent only 
an id.
+   */
+  public final void writeClassDefs(MemoryBuffer buffer) {
+    MetaContext metaContext = fory.getSerializationContext().getMetaContext();
+    ObjectArray<ClassDef> writingClassDefs = metaContext.writingClassDefs;
+    final int size = writingClassDefs.size;
+    buffer.writeVarUint32Small7(size);
+    if (buffer.isHeapFullyWriteable()) {
+      writeClassDefs(buffer, writingClassDefs, size);
+    } else {
+      for (int i = 0; i < size; i++) {
+        writingClassDefs.get(i).writeClassDef(buffer);
+      }
+    }
+    metaContext.writingClassDefs.size = 0;
+  }
+
+  private void writeClassDefs(
+      MemoryBuffer buffer, ObjectArray<ClassDef> writingClassDefs, int size) {
+    for (int i = 0; i < size; i++) {
+      buffer.writeBytes(writingClassDefs.get(i).getEncoded());
+      MemoryBuffer memoryBuffer = 
MemoryBuffer.fromByteArray(writingClassDefs.get(i).getEncoded());
+      ClassDef.readClassDef(fory, memoryBuffer, memoryBuffer.readInt64());
+    }
+  }
+
+  /**
+   * Ensure all class definition are read and populated, even there are 
deserialization exception
+   * such as ClassNotFound. So next time a class def written previously 
identified by an id can be
+   * got from the meta context.
+   */
+  public final void readClassDefs(MemoryBuffer buffer) {
+    MetaContext metaContext = fory.getSerializationContext().getMetaContext();
+    assert metaContext != null : SET_META__CONTEXT_MSG;
+    int numClassDefs = buffer.readVarUint32Small7();
+    for (int i = 0; i < numClassDefs; i++) {
+      long id = buffer.readInt64();
+      Tuple2<ClassDef, ClassInfo> tuple2 = extRegistry.classIdToDef.get(id);
+      if (tuple2 != null) {
+        ClassDef.skipClassDef(buffer, id);
+      } else {
+        tuple2 = readClassDef(buffer, id);
+      }
+      metaContext.readClassDefs.add(tuple2.f0);
+      metaContext.readClassInfos.add(tuple2.f1);
+    }
+  }
+
+  private Tuple2<ClassDef, ClassInfo> readClassDef(MemoryBuffer buffer, long 
header) {
+    ClassDef readClassDef = ClassDef.readClassDef(fory, buffer, header);
+    Tuple2<ClassDef, ClassInfo> tuple2 = 
extRegistry.classIdToDef.get(readClassDef.getId());
+    if (tuple2 == null) {
+      tuple2 = putClassDef(readClassDef, null);
+    }
+    return tuple2;
+  }
+
+  private Tuple2<ClassDef, ClassInfo> putClassDef(ClassDef classDef, ClassInfo 
classInfo) {
+    Tuple2<ClassDef, ClassInfo> tuple2 = Tuple2.of(classDef, classInfo);
+    extRegistry.classIdToDef.put(classDef.getId(), tuple2);
+    return tuple2;
+  }
+
+  final Class<?> loadClass(ClassSpec classSpec) {
+    if (classSpec.type != null) {
+      return classSpec.type;
+    }
+    return loadClass(classSpec.entireClassName, classSpec.isEnum, 
classSpec.dimension);
+  }
+
+  final Class<?> loadClass(String className, boolean isEnum, int arrayDims) {
+    return loadClass(className, isEnum, arrayDims, 
fory.getConfig().deserializeNonexistentClass());
+  }
+
+  final Class<?> loadClass(
+      String className, boolean isEnum, int arrayDims, boolean 
deserializeNonexistentClass) {
+    extRegistry.typeChecker.checkType(this, className);
+    Class<?> cls = extRegistry.registeredClasses.get(className);
+    if (cls != null) {
+      return cls;
+    }
+    try {
+      return Class.forName(className, false, fory.getClassLoader());
+    } catch (ClassNotFoundException e) {
+      try {
+        return Class.forName(className, false, 
Thread.currentThread().getContextClassLoader());
+      } catch (ClassNotFoundException ex) {
+        String msg =
+            String.format(
+                "Class %s not found from classloaders [%s, %s]",
+                className, fory.getClassLoader(), 
Thread.currentThread().getContextClassLoader());
+        if (deserializeNonexistentClass) {
+          LOG.warn(msg);
+          return NonexistentClass.getNonexistentClass(
+              className, isEnum, arrayDims, metaContextShareEnabled);
+        }
+        throw new IllegalStateException(msg, ex);
+      }
+    }
+  }
+
   public abstract <T> Serializer<T> getSerializer(Class<T> cls);
 
   public abstract Serializer<?> getRawSerializer(Class<?> cls);
@@ -123,7 +430,7 @@ public abstract class TypeResolver {
 
   public abstract Class<? extends Serializer> getSerializerClass(Class<?> cls, 
boolean codegen);
 
-  public boolean isCollection(Class<?> cls) {
+  public final boolean isCollection(Class<?> cls) {
     if (Collection.class.isAssignableFrom(cls)) {
       return true;
     }
@@ -138,7 +445,7 @@ public abstract class TypeResolver {
     }
   }
 
-  public boolean isSet(Class<?> cls) {
+  public final boolean isSet(Class<?> cls) {
     if (Set.class.isAssignableFrom(cls)) {
       return true;
     }
@@ -153,7 +460,7 @@ public abstract class TypeResolver {
     }
   }
 
-  public boolean isMap(Class<?> cls) {
+  public final boolean isMap(Class<?> cls) {
     if (cls == NonexistentMetaShared.class) {
       return false;
     }
@@ -162,7 +469,7 @@ public abstract class TypeResolver {
             && ScalaTypes.getScalaMapType().isAssignableFrom(cls));
   }
 
-  public DescriptorGrouper createDescriptorGrouper(
+  public final DescriptorGrouper createDescriptorGrouper(
       Collection<Descriptor> descriptors, boolean descriptorsGroupedOrdered) {
     return createDescriptorGrouper(descriptors, descriptorsGroupedOrdered, 
null);
   }
@@ -181,7 +488,7 @@ public abstract class TypeResolver {
    *     fields in the class
    * @return a map of nested generic type name to generic type for all fields 
in the class
    */
-  protected Map<String, GenericType> buildGenericMap(Class<?> cls) {
+  protected final Map<String, GenericType> buildGenericMap(Class<?> cls) {
     Map<String, GenericType> map = new HashMap<>();
     Map<String, GenericType> map2 = new HashMap<>();
     for (Field field : ReflectionUtils.getFields(cls, true)) {
@@ -224,7 +531,135 @@ public abstract class TypeResolver {
     }
   }
 
+  public void setTypeChecker(TypeChecker typeChecker) {
+    extRegistry.typeChecker = typeChecker;
+  }
+
+  private static final ConcurrentMap<Integer, GraalvmClassRegistry> 
GRAALVM_REGISTRY =
+      new ConcurrentHashMap<>();
+
+  // CHECKSTYLE.OFF:MethodName
+  public static void _addGraalvmClassRegistry(int foryConfigHash, 
ClassResolver classResolver) {
+    // CHECKSTYLE.ON:MethodName
+    if (GraalvmSupport.isGraalBuildtime()) {
+      GraalvmClassRegistry registry =
+          GRAALVM_REGISTRY.computeIfAbsent(foryConfigHash, k -> new 
GraalvmClassRegistry());
+      registry.resolvers.add(classResolver);
+    }
+  }
+
+  static class GraalvmClassRegistry {
+    final List<ClassResolver> resolvers;
+    final Map<Class<?>, Class<? extends Serializer>> serializerClassMap;
+    final Map<Long, Class<? extends Serializer>> deserializerClassMap;
+
+    private GraalvmClassRegistry() {
+      resolvers = Collections.synchronizedList(new ArrayList<>());
+      serializerClassMap = new ConcurrentHashMap<>();
+      deserializerClassMap = new ConcurrentHashMap<>();
+    }
+  }
+
+  final GraalvmClassRegistry getGraalvmClassRegistry() {
+    return GRAALVM_REGISTRY.computeIfAbsent(
+        fory.getConfig().getConfigHash(), k -> new GraalvmClassRegistry());
+  }
+
+  final Class<? extends Serializer> getGraalvmSerializerClass(Serializer 
serializer) {
+    if (serializer instanceof GraalvmSerializerHolder) {
+      return ((GraalvmSerializerHolder) serializer).getSerializerClass();
+    }
+    return serializer.getClass();
+  }
+
+  final Class<? extends Serializer> 
getSerializerClassFromGraalvmRegistry(Class<?> cls) {
+    GraalvmClassRegistry registry = getGraalvmClassRegistry();
+    List<ClassResolver> classResolvers = registry.resolvers;
+    if (classResolvers.isEmpty()) {
+      return null;
+    }
+    for (ClassResolver classResolver : classResolvers) {
+      if (classResolver != this) {
+        ClassInfo classInfo = getClassInfo(cls, false);
+        if (classInfo != null && classInfo.serializer != null) {
+          return classInfo.serializer.getClass();
+        }
+      }
+    }
+    Class<? extends Serializer> serializerClass = 
registry.serializerClassMap.get(cls);
+    // noinspection Duplicates
+    if (serializerClass != null) {
+      return serializerClass;
+    }
+    if (GraalvmSupport.isGraalRuntime()) {
+      if (Functions.isLambda(cls) || ReflectionUtils.isJdkProxy(cls)) {
+        return null;
+      }
+      throw new RuntimeException(String.format("Class %s is not registered", 
cls));
+    }
+    return null;
+  }
+
+  private Class<? extends Serializer> 
getMetaSharedDeserializerClassFromGraalvmRegistry(
+      Class<?> cls, ClassDef classDef) {
+    GraalvmClassRegistry registry = getGraalvmClassRegistry();
+    List<ClassResolver> classResolvers = registry.resolvers;
+    if (classResolvers.isEmpty()) {
+      return null;
+    }
+    Class<? extends Serializer> deserializerClass =
+        registry.deserializerClassMap.get(classDef.getId());
+    // noinspection Duplicates
+    if (deserializerClass != null) {
+      return deserializerClass;
+    }
+    if (GraalvmSupport.isGraalRuntime()) {
+      if (Functions.isLambda(cls) || ReflectionUtils.isJdkProxy(cls)) {
+        return null;
+      }
+      throw new RuntimeException(
+          String.format(
+              "Class %s is not registered, registered classes: %s",
+              cls, registry.deserializerClassMap));
+    }
+    return null;
+  }
+
   public final Fory getFory() {
     return fory;
   }
+
+  public final MetaStringResolver getMetaStringResolver() {
+    return metaStringResolver;
+  }
+
+  static class ExtRegistry {
+    // Here we set it to 1 because `NO_CLASS_ID` is 0 to avoid calculating it 
again in
+    // `register(Class<?> cls)`.
+    short classIdGenerator = 1;
+    SerializerFactory serializerFactory;
+    final IdentityMap<Class<?>, Short> registeredClassIdMap =
+        new IdentityMap<>(estimatedNumRegistered);
+    final BiMap<String, Class<?>> registeredClasses = 
HashBiMap.create(estimatedNumRegistered);
+    // cache absClassInfo, support customized serializer for abstract or 
interface.
+    final IdentityMap<Class<?>, ClassInfo> absClassInfo =
+        new IdentityMap<>(estimatedNumRegistered, foryMapLoadFactor);
+    // avoid potential recursive call for seq codec generation.
+    // ex. A->field1: B, B.field1: A
+    final Set<Class<?>> getClassCtx = new HashSet<>();
+    final Map<Class<?>, FieldResolver> fieldResolverMap = new HashMap<>();
+    final LongMap<Tuple2<ClassDef, ClassInfo>> classIdToDef = new LongMap<>();
+    final Map<Class<?>, ClassDef> currentLayerClassDef = new HashMap<>();
+    // Tuple2<Class, Class>: Tuple2<From Class, To Class>
+    final Map<Tuple2<Class<?>, Class<?>>, ClassInfo> transformedClassInfo = 
new HashMap<>();
+    // TODO(chaokunyang) Better to  use soft reference, see ObjectStreamClass.
+    final ConcurrentHashMap<Tuple2<Class<?>, Boolean>, SortedMap<Member, 
Descriptor>>
+        descriptorsCache = new ConcurrentHashMap<>();
+    TypeChecker typeChecker = (resolver, className) -> true;
+    GenericType objectGenericType;
+    final IdentityMap<Type, GenericType> genericTypes = new IdentityMap<>();
+    final Map<Class, Map<String, GenericType>> classGenericTypes = new 
HashMap<>();
+    final Map<List<ClassLoader>, CodeGenerator> codeGeneratorMap = new 
HashMap<>();
+    final Set<ClassInfo> initialClassInfos = new HashSet<>();
+  }
 }
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java 
b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
index fb51af749..e5a49743c 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
@@ -25,7 +25,6 @@ import static org.apache.fory.meta.Encoders.GENERIC_ENCODER;
 import static org.apache.fory.meta.Encoders.PACKAGE_DECODER;
 import static org.apache.fory.meta.Encoders.PACKAGE_ENCODER;
 import static org.apache.fory.meta.Encoders.TYPE_NAME_DECODER;
-import static org.apache.fory.resolver.ClassResolver.NO_CLASS_ID;
 import static 
org.apache.fory.serializer.collection.MapSerializers.HashMapSerializer;
 import static org.apache.fory.type.TypeUtils.qualifiedName;
 
@@ -54,7 +53,6 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
 import org.apache.fory.Fory;
 import org.apache.fory.annotation.Internal;
-import org.apache.fory.collection.IdentityMap;
 import org.apache.fory.collection.IdentityObjectIntMap;
 import org.apache.fory.collection.LongMap;
 import org.apache.fory.collection.ObjectMap;
@@ -110,10 +108,9 @@ public class XtypeResolver extends TypeResolver {
   private final Config config;
   private final Fory fory;
   private final ClassResolver classResolver;
-  private final ClassInfoHolder classInfoCache = new 
ClassInfoHolder(ClassResolver.NIL_CLASS_INFO);
+  private final ClassInfoHolder classInfoCache = new 
ClassInfoHolder(NIL_CLASS_INFO);
   private final MetaStringResolver metaStringResolver;
-  // IdentityMap has better lookup performance, when loadFactor is 0.05f, 
performance is better
-  private final IdentityMap<Class<?>, ClassInfo> classInfoMap = new 
IdentityMap<>(64, loadFactor);
+
   // Every deserialization for unregistered class will query it, performance 
is important.
   private final ObjectMap<TypeNameBytes, ClassInfo> 
compositeClassNameBytes2ClassInfo =
       new ObjectMap<>(16, loadFactor);
@@ -244,7 +241,9 @@ public class XtypeResolver extends TypeResolver {
   private void register(
       Class<?> type, Serializer<?> serializer, String namespace, String 
typeName, int xtypeId) {
     ClassInfo classInfo = newClassInfo(type, serializer, namespace, typeName, 
(short) xtypeId);
-    qualifiedType2ClassInfo.put(qualifiedName(namespace, typeName), classInfo);
+    String qualifiedName = qualifiedName(namespace, typeName);
+    qualifiedType2ClassInfo.put(qualifiedName, classInfo);
+    extRegistry.registeredClasses.put(qualifiedName, type);
     if (serializer == null) {
       if (type.isEnum()) {
         classInfo.serializer = new EnumSerializer(fory, (Class<Enum>) type);
@@ -464,15 +463,6 @@ public class XtypeResolver extends TypeResolver {
     return xtypeIdToClassMap.get(userTypeId);
   }
 
-  @Override
-  public boolean needToWriteRef(TypeRef<?> typeRef) {
-    ClassInfo classInfo = classInfoMap.get(typeRef.getRawType());
-    if (classInfo == null) {
-      return fory.trackingRef();
-    }
-    return classInfo.serializer.needToWriteRef();
-  }
-
   @Override
   public GenericType buildGenericType(TypeRef<?> typeRef) {
     return classResolver.buildGenericType(typeRef);
@@ -644,7 +634,7 @@ public class XtypeResolver extends TypeResolver {
 
   public void writeSharedClassMeta(MemoryBuffer buffer, ClassInfo classInfo) {
     MetaContext metaContext = fory.getSerializationContext().getMetaContext();
-    assert metaContext != null : ClassResolver.SET_META__CONTEXT_MSG;
+    assert metaContext != null : SET_META__CONTEXT_MSG;
     IdentityObjectIntMap<Class<?>> classMap = metaContext.classMap;
     int newId = classMap.size;
     int id = classMap.putOrGet(classInfo.cls, newId);
@@ -739,13 +729,18 @@ public class XtypeResolver extends TypeResolver {
     }
   }
 
+  @Override
+  public ClassInfo readSharedClassMeta(MemoryBuffer buffer, MetaContext 
metaContext) {
+    return readClassInfo(buffer);
+  }
+
   private ClassInfo readSharedClassMeta(MemoryBuffer buffer) {
     MetaContext metaContext = fory.getSerializationContext().getMetaContext();
-    assert metaContext != null : ClassResolver.SET_META__CONTEXT_MSG;
+    assert metaContext != null : SET_META__CONTEXT_MSG;
     int id = buffer.readVarUint32Small14();
     ClassInfo classInfo = metaContext.readClassInfos.get(id);
     if (classInfo == null) {
-      classInfo = classResolver.readClassInfoWithMetaShare(metaContext, id);
+      classInfo = readSharedClassMeta(metaContext, id);
     }
     return classInfo;
   }
diff --git 
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
 
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
index 9be63527c..2e908e419 100644
--- 
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
+++ 
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
@@ -267,11 +267,12 @@ 
Args=--initialize-at-build-time=org.apache.fory.memory.MemoryBuffer,\
     org.apache.fory.reflect.TypeRef$TypeVariableKey,\
     org.apache.fory.reflect.TypeRef,\
     org.apache.fory.resolver.ClassChecker,\
+    org.apache.fory.resolver.TypeChecker,\
     org.apache.fory.resolver.ClassInfo,\
     org.apache.fory.resolver.ClassInfoHolder,\
     org.apache.fory.resolver.ClassResolver$2,\
-    org.apache.fory.resolver.ClassResolver$ExtRegistry,\
-    org.apache.fory.resolver.ClassResolver$GraalvmClassRegistry,\
+    org.apache.fory.resolver.TypeResolver$ExtRegistry,\
+    org.apache.fory.resolver.TypeResolver$GraalvmClassRegistry,\
     org.apache.fory.resolver.ClassResolver,\
     org.apache.fory.resolver.DisallowedList,\
     org.apache.fory.resolver.FieldResolver$CollectionFieldInfo,\
diff --git 
a/java/fory-core/src/test/java/org/apache/fory/CrossLanguageTest.java 
b/java/fory-core/src/test/java/org/apache/fory/CrossLanguageTest.java
index 85f6ed17a..ea33695e9 100644
--- a/java/fory-core/src/test/java/org/apache/fory/CrossLanguageTest.java
+++ b/java/fory-core/src/test/java/org/apache/fory/CrossLanguageTest.java
@@ -863,6 +863,27 @@ public class CrossLanguageTest extends ForyTestBase {
     structRoundBack(fory, a, "test_enum_field" + (compatible ? "_compatible" : 
""));
   }
 
+  @Test(dataProvider = "compatible")
+  public void testNamedEnum(boolean compatible) {
+    Fory fory =
+        Fory.builder()
+            // avoid generated code conflict with register by name
+            .withName("testEnumObject")
+            .withLanguage(Language.XLANG)
+            .withCompatibleMode(
+                compatible ? CompatibleMode.COMPATIBLE : 
CompatibleMode.SCHEMA_CONSISTENT)
+            .requireClassRegistration(true)
+            .build();
+    fory.register(EnumTestClass.class, "demo.Enum1");
+    fory.register(EnumFieldStruct.class, "demo.EnumFieldStruct");
+    Assert.assertEquals(xserDe(fory, EnumTestClass.FOO), EnumTestClass.FOO);
+    EnumFieldStruct a = new EnumFieldStruct();
+    a.f1 = EnumTestClass.FOO;
+    a.f2 = EnumTestClass.BAR;
+    a.f3 = "abc";
+    Assert.assertEquals(xserDe(fory, a), a);
+  }
+
   @Test(dataProvider = "compatible")
   public void testEnumFieldRegisterById(boolean compatible) throws 
java.io.IOException {
     Fory fory =
diff --git a/java/fory-core/src/test/java/org/apache/fory/ForyCopyTest.java 
b/java/fory-core/src/test/java/org/apache/fory/ForyCopyTest.java
index 5d0219df6..5c68851fd 100644
--- a/java/fory-core/src/test/java/org/apache/fory/ForyCopyTest.java
+++ b/java/fory-core/src/test/java/org/apache/fory/ForyCopyTest.java
@@ -142,7 +142,7 @@ public class ForyCopyTest extends ForyTestBase {
     AtomicReference<Throwable> ex = new AtomicReference<>();
     ThreadLocalFory threadLocalFory =
         builder().withCodegen(false).withRefCopy(true).buildThreadLocalFory();
-    threadLocalFory.setClassChecker((classResolver, className1) -> true);
+    threadLocalFory.setTypeChecker((classResolver, className1) -> true);
     threadLocalFory.setSerializerFactory((fory1, cls) -> null);
     threadLocalFory.register(BeanA.class);
     assetEqualsButNotSame(threadLocalFory.copy(beanA));
diff --git 
a/java/fory-core/src/test/java/org/apache/fory/ThreadSafeForyTest.java 
b/java/fory-core/src/test/java/org/apache/fory/ThreadSafeForyTest.java
index fa1d9a995..dd8697e8c 100644
--- a/java/fory-core/src/test/java/org/apache/fory/ThreadSafeForyTest.java
+++ b/java/fory-core/src/test/java/org/apache/fory/ThreadSafeForyTest.java
@@ -256,7 +256,7 @@ public class ThreadSafeForyTest extends ForyTestBase {
     CompletableFuture.runAsync(
             () -> {
               fory.setClassLoader(structClass1.getClassLoader(), staging);
-              fory.setClassChecker((classResolver, className1) -> true);
+              fory.setTypeChecker((classResolver, className1) -> true);
               fory.setSerializerFactory((fory1, cls) -> null);
               Assert.assertEquals(fory.deserialize(newBytes1), struct1);
             })
diff --git 
a/java/fory-core/src/test/java/org/apache/fory/resolver/AllowListCheckerTest.java
 
b/java/fory-core/src/test/java/org/apache/fory/resolver/AllowListCheckerTest.java
index 28ec68f45..28289d104 100644
--- 
a/java/fory-core/src/test/java/org/apache/fory/resolver/AllowListCheckerTest.java
+++ 
b/java/fory-core/src/test/java/org/apache/fory/resolver/AllowListCheckerTest.java
@@ -34,7 +34,7 @@ public class AllowListCheckerTest {
     {
       Fory fory = Fory.builder().requireClassRegistration(false).build();
       AllowListChecker checker = new 
AllowListChecker(AllowListChecker.CheckLevel.STRICT);
-      fory.getClassResolver().setClassChecker(checker);
+      fory.getClassResolver().setTypeChecker(checker);
       assertThrows(InsecureException.class, () -> fory.serialize(new 
AllowListCheckerTest()));
       checker.allowClass(AllowListCheckerTest.class.getName());
       byte[] bytes = fory.serialize(new AllowListCheckerTest());
@@ -46,7 +46,7 @@ public class AllowListCheckerTest {
     {
       Fory fory = Fory.builder().requireClassRegistration(false).build();
       AllowListChecker checker = new 
AllowListChecker(AllowListChecker.CheckLevel.WARN);
-      fory.getClassResolver().setClassChecker(checker);
+      fory.getClassResolver().setTypeChecker(checker);
       checker.addListener(fory.getClassResolver());
       byte[] bytes = fory.serialize(new AllowListCheckerTest());
       checker.disallowClass(AllowListCheckerTest.class.getName());
@@ -60,7 +60,7 @@ public class AllowListCheckerTest {
     {
       Fory fory = Fory.builder().requireClassRegistration(false).build();
       AllowListChecker checker = new 
AllowListChecker(AllowListChecker.CheckLevel.STRICT);
-      fory.getClassResolver().setClassChecker(checker);
+      fory.getClassResolver().setTypeChecker(checker);
       checker.addListener(fory.getClassResolver());
       assertThrows(InsecureException.class, () -> fory.serialize(new 
AllowListCheckerTest()));
       checker.allowClass("org.apache.fory.*");
@@ -72,7 +72,7 @@ public class AllowListCheckerTest {
     {
       Fory fory = Fory.builder().requireClassRegistration(false).build();
       AllowListChecker checker = new 
AllowListChecker(AllowListChecker.CheckLevel.WARN);
-      fory.getClassResolver().setClassChecker(checker);
+      fory.getClassResolver().setTypeChecker(checker);
       checker.addListener(fory.getClassResolver());
       byte[] bytes = fory.serialize(new AllowListCheckerTest());
       checker.disallowClass("org.apache.fory.*");
@@ -92,7 +92,7 @@ public class AllowListCheckerTest {
                       .requireClassRegistration(false)
                       .withClassLoader(classLoader)
                       .build();
-              f.getClassResolver().setClassChecker(checker);
+              f.getClassResolver().setTypeChecker(checker);
               checker.addListener(f.getClassResolver());
               return f;
             });


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to