docs: protected static <T> Codec<T> doCreate(Class<T> cls, boolean debug, Compiler compiler, ICodeGenerator cg) {
          if (cls == null) {
              throw new NullPointerException("Parameter cls is null");
          }
  
          Ignore ignore = cls.getAnnotation(Ignore.class);
          if (ignore != null) {
              LOGGER.info("class '{}' marked as @Ignore annotation, proxy ignored.", cls.getName());
              return null;
          }
  
          String uniClsName = cls.getName();
          if (isCacheEnabled()) {
              Codec codec = CACHED.get(uniClsName);
              if (codec != null) {
                  return codec;
              }
          }
  
          // crate code generator
          cg.setDebug(debug);
          File path = OUTPUT_PATH.get();
          cg.setOutputPath(path);
  
          // try to load first
          String className = cg.getFullClassName();
          Class<?> c = null;
          try {
              c = Class.forName(className, true, getClassLoader());
          } catch (ClassNotFoundException e1) {
              try {
                  c = Class.forName(className, true, ProtobufProxy.class.getClassLoader());
              } catch (ClassNotFoundException e2) {
                  // if class not found so should generate a new java source class.
                  c = null;
              }
          }
  
          if (c != null) {
              try {
                  Codec<T> newInstance = (Codec<T>) c.newInstance();
                  if (!CACHED.containsKey(uniClsName)) {
                      CACHED.put(uniClsName, newInstance);
                  }
                  return newInstance;
              } catch (InstantiationException e) {
                  throw new RuntimeException(e.getMessage(), e);
              } catch (IllegalAccessException e) {
                  throw new RuntimeException(e.getMessage(), e);
              }
          }
  
          String code = cg.getCode();
          if (debug) {
              String printCode = code;
              CodePrinter.printCode(printCode, "generate protobuf proxy code");
          }
  
          FileOutputStream fos = null;
          if (path != null && path.isDirectory()) {
              String pkg = "";
              if (className.indexOf('.') != -1) {
                  pkg = StringUtils.substringBeforeLast(className, ".");
              }
  
              // mkdirs
              String dir = path + File.separator + pkg.replace('.', File.separatorChar);
              File f = new File(dir);
              f.mkdirs();
  
              try {
                  fos = new FileOutputStream(new File(f, cg.getClassName() + ".class"));
              } catch (Exception e) {
                  throw new RuntimeException(e.getMessage(), e);
              }
  
          }
  
          if (compiler == null) {
              compiler = JDKCompilerHelper.getJdkCompiler();
          }
          Class<?> newClass;
  
          // get last modify time
          long lastModify = ClassHelper.getLastModifyTime(cls);
  
          try {
              newClass = compiler.compile(className, code, cls.getClassLoader(), fos, lastModify);
          } catch (Exception e) {
              compiler = JDKCompilerHelper.getJdkCompiler(cls.getClassLoader());
              newClass = compiler.compile(className, code, cls.getClassLoader(), fos, lastModify);
          }
  
          if (fos != null) {
              try {
                  fos.close();
              } catch (IOException e) {
                  throw new RuntimeException(e.getMessage(), e);
              }
          }
  
          try {
              Codec<T> newInstance = (Codec<T>) newClass.newInstance();
              if (!CACHED.containsKey(uniClsName)) {
                  CACHED.put(uniClsName, newInstance);
              }
  
              try {
                  // try to eagle load
                  Set<Class<?>> relativeProxyClasses = cg.getRelativeProxyClasses();
                  for (Class<?> relativeClass : relativeProxyClasses) {
                      ProtobufProxy.create(relativeClass, debug, path, compiler, cg);
                  }
              } catch (Exception e) {
                  if (LOGGER.isDebugEnabled()) {
                      LOGGER.debug(e.getMessage(), e);
                  }
              }
  
              return newInstance;
          } catch (InstantiationException e) {
              throw new RuntimeException(e.getMessage(), e);
          } catch (IllegalAccessException e) {
              throw new RuntimeException(e.getMessage(), e);
          }
      }