MyBatis源碼解析 - 反射模塊

来源:https://www.cnblogs.com/liuzhiyong0718/archive/2020/03/12/12466610.html
-Advertisement-
Play Games

MyBatis源碼解析 反射模塊 1. 前言 ​ 該模塊位於 包中,MyBatis在進行參數處理、結果映射等操作時,會涉及大量的反射操作。Java 中的反射雖然功能強大,但是代碼編寫起來比較複雜且容易出錯,為了簡化反射操作的相關代碼,MyBatis提供了專門的反射模塊,它對常見的反射操作做了進一步封 ...


MyBatis源碼解析 - 反射模塊

1. 前言

​ 該模塊位於org.apache.ibatis.reflection包中,MyBatis在進行參數處理、結果映射等操作時,會涉及大量的反射操作。Java 中的反射雖然功能強大,但是代碼編寫起來比較複雜且容易出錯,為了簡化反射操作的相關代碼,MyBatis提供了專門的反射模塊,它對常見的反射操作做了進一步封裝,提供了更加簡潔方便的反射API。本節就來為讀者介紹該模塊中核心代碼的實現。

2. Reflector

Reflector 是MyBaits中反射模塊的基礎,Reflector中緩存了很多反射操作需要使用的元數據。各個欄位如下:

private final Class<?> type;  //對應的class類型
private final String[] readablePropertyNames; //可讀屬性的名稱集合,可讀屬性就是存在相應getter方法的屬性
private final String[] writablePropertyNames; //可寫屬性的名稱集合,可寫屬性就是存在相應setter方法的屬性
//記錄了屬性相應的setter方法,key是屬性名稱,value 是Invoker對象,它是對setter方法對應
// Method 對象的封裝
private final Map<String, Invoker> setMethods = new HashMap<>();
//屬性相應的getter方法集合,key 是屬性名稱,value 也是Invoker對象
private final Map<String, Invoker> getMethods = new HashMap<>();
//記錄了屬性相應的setter方法的參數值類型,key是屬性名稱,value是setter方法的參數類型
private final Map<String, Class<?>> setTypes = new HashMap<>();
//記錄了屬性相應的getter方法的參數值類型,key是屬性名稱,value是getter方法的參數類型
private final Map<String, Class<?>> getTypes = new HashMap<>();
//記錄了預設的構造函數
private Constructor<?> defaultConstructor;
//記錄了所有屬性名稱的集合
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

在Reflector的構造函數中,會解析指定的Class對象,並填充上述集合,代碼如下:

public Reflector(Class<?> clazz) {
    type = clazz; //初始化type欄位
    //查找預設構造函數(無參構造函數)具體實現是以反射遍歷所有構造方法
    addDefaultConstructor(clazz);
    //處理class中的getter方法
    addGetMethods(clazz);
    //處理class中的setter方法
    addSetMethods(clazz);
    //處理沒有getter或setter方法的欄位
    addFields(clazz);
    // 初始化為空數組
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    // 初始化為空數組
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    //初始化caseInsensitivePropertyMap集合 其中記錄了所有大寫格式的屬性名稱
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

Reflector.addGetMethods()方法主要解析類中定義的getter方法,Reflector.addSetMethods()方法主要解析類中定義的setter方法,兩個類似,下麵我們就以Reflector.addGetMethods()方法為例進行介紹,Reflector.addGetMethods()解析有下麵三個步驟:

  1. 首先調用Reflector.getClassMethods()方法獲取當前類以及父類中定義的所有方法的唯一簽名以及相應的Method對象。

    private Method[] getClassMethods(Class<?> clazz) {
      //用於記錄指定類中定義的全部方法的唯一簽名對應的Method對象
      Map<String, Method> uniqueMethods = new HashMap<>();
      Class<?> currentClass = clazz;
      while (currentClass != null && currentClass != Object.class) {
        //記錄currentClass類中定義的全部方法
        addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
    
        // we also need to look for interface methods -
        // because the class may be abstract
        //記錄介面中定義一的方法
        Class<?>[] interfaces = currentClass.getInterfaces();
        for (Class<?> anInterface : interfaces) {
          addUniqueMethods(uniqueMethods, anInterface.getMethods());
        }
        //獲取父類繼續while迴圈
        currentClass = currentClass.getSuperclass();
      }
    
      Collection<Method> methods = uniqueMethods.values();
      //轉換成methods數組返回
      return methods.toArray(new Method[0]);
    }

    在Reflector.getClassMethods()方法中會為每個方法生成唯一簽名,並記錄到conflictingSetters集合中,實現如下:


    private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
      for (Method currentMethod : methods) {
        if (!currentMethod.isBridge()) {
          //通過getSignature()方法得到的方法簽名 規則:返回值類型#方法名稱:參數類型列表。
          //通過getSignature()方法得到的發放簽名是全局唯一的,可以作為該方法的唯一標識
          String signature = getSignature(currentMethod);
          // check to see if the method is already known
          // if it is known, then an extended class must have
          // overridden a method
          //檢測是否在子類中已經添加過該方法【如果添加過就忽略】
          if (!uniqueMethods.containsKey(signature)) {
            //記錄該簽名和方法的對應關係
            uniqueMethods.put(signature, currentMethod);
          }
        }
      }
    }

  2. 然後按照Java規範,從Reflector.getClassMethods()方法返回的Method數組中查找該類的getter方法,記錄到conflictingSetters集合中。【該集合為HashMap<String,<List<Method>>>,key為屬性名稱,value是該屬性對應的setter集合。】

  3. 當子類覆蓋父類的getter方法且返回值發生變化時,在步驟【1】中就會產生兩個簽名不同的方法,在Reflector.addUniqueMethods()方法中會被當做兩個不同的方法添加到conflictingSetters集合中,這顯然不是我們要的結果,所以第三步我們會調用Reflector.resolveSetterConflicts()方法對這個覆寫的方法進行處理,同時會將得到的getter方法記錄到getMethods集合,並將其返回值類型填充到getTypes集合。Reflector.resolveSetterConflicts()方法具體實現如下:


    private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
      // 遍歷 conflictingGetters 集合
      for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
        Method winner = null;
        //獲取屬性名
        String propName = entry.getKey();
        //是否是不明確的
        boolean isAmbiguous = false;
        //
        for (Method candidate : entry.getValue()) {
          if (winner == null) {
            winner = candidate;
            continue;
          }
          //獲取最終選擇的方法方法返回值
          Class<?> winnerType = winner.getReturnType();
          //獲取候選方法候選類型
          Class<?> candidateType = candidate.getReturnType();
          //返回值相同
          if (candidateType.equals(winnerType)) {
            //對於不是布爾類型的
            if (!boolean.class.equals(candidateType)) {
              //不明確的類型 【不明確 拋異常】
              isAmbiguous = true;
              break;
            } else if (candidate.getName().startsWith("is")) {
              //對於boolean類型
              //當前方法返回值是當前最合適的方法的返回值的子類
              winner = candidate;
            }
          } else if (candidateType.isAssignableFrom(winnerType)) {
            //當前最適合的方法的返回值是當前方法返回值的子類,什麼都不做 當前最適合的方法 依然不變
            // OK getter type is descendant
          } else if (winnerType.isAssignableFrom(candidateType)) {  //當前返回值是當前最適合的方法的返回值的
            winner = candidate;
          } else {
            //不明確的類型 要拋異常
            isAmbiguous = true;
            break;
          }
        }
        addGetMethod(propName, winner, isAmbiguous);
      }
    }

    Reflector.addGetMethod()方法對getMethods集合和getTypes集合進行填充,具體實現代碼如下:


    private void addGetMethod(String name, Method method, boolean isAmbiguous) {
      //對於不明確的方法 拋異常處理
      MethodInvoker invoker = isAmbiguous
          ? new AmbiguousMethodInvoker(method, MessageFormat.format(
              "Illegal overloaded getter method with ambiguous type for property ''{0}'' in class ''{1}''. This breaks the JavaBeans specification and can cause unpredictable results.",
              name, method.getDeclaringClass().getName()))
          : new MethodInvoker(method);
      //將屬性名以及對應的MethodInvoker對象添加到getMethods集合中,Invoker的內容後面解析
      getMethods.put(name, invoker);
      //獲取返回值的Type,TypeParameterResolver會在後面分析
      Type returnType = TypeParameterResolver.resolveReturnType(method, type);
      //將屬性名及其getter方法的返回值加到getTypes集合中保存,typeToClass()方法後面分析
      getTypes.put(name, typeToClass(returnType));
    }

    分析完Reflector.addGetMethods()的三個核心步驟後,我們看下具體實現代碼,如下:


    private void addGetMethods(Class<?> clazz) {
      //conflictingGetters集合 key為屬性名稱,value為相應的getter方法集合,因為子類可能覆蓋父類的getter方法,
      //所以同一屬性的名稱可能存在多個getter方法
      Map<String, List<Method>> conflictingGetters = new HashMap<>();
      //步驟一:獲取指定類以及父類和介面中定義的方法
      Method[] methods = getClassMethods(clazz);
      //步驟二:按照javabean規範查找getter方法 添加到conflictingGetters中
      Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
        .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
      //對於getter方法進行處理
      resolveGetterConflicts(conflictingGetters);
    

    Reflector.addFields()方法會處理類中定義的所有欄位,並且將處理的欄位信息添加到setMethods集合、setTypes集合、getMethods集合、getTypes集合中,Reflector.addFileds()方法的實現代碼如下:

    //處理類中定義的所有欄位
    private void addFields(Class<?> clazz) {
      //提取clazz中定義的所有欄位
      Field[] fields = clazz.getDeclaredFields();
      for (Field field : fields) {
        if (!setMethods.containsKey(field.getName())) {
          // issue #379 - removed the check for final because JDK 1.5 allows
          // modification of final fields through reflection (JSR-133). (JGB)
          // pr #16 - final static can only be set by the classloader
          int modifiers = field.getModifiers();
          //過濾調static和final修飾的方法
          if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
            //填充setMethods集合和setTypes集合
            addSetField(field);
          }
        }
        //getMethods集合中不包同名屬性時,將其記錄到getMethods集合和getTypes集合中
        if (!getMethods.containsKey(field.getName())) {
          //填充getMethods集合和getTypes集合
          addGetField(field);
        }
      }
      if (clazz.getSuperclass() != null) {
        //處理父類中定義的欄位
        addFields(clazz.getSuperclass());
      }
    }

    Reflector中提供多個get *()方法用於讀取上述集合中的元數據信息。

3. Invoker & GetFieldInvoker & SetFieldInvoker & AmbiguousMethodInvoker & MethodInvoker

add Methods()方法和add Field()方法在上述集合中添加元素的時候,會將getter/setter方法對應的Method對象以及欄位對應的Field對象統一封裝成Invoker對象。Invoker介面的定義如下:


public interface Invoker {
  //調用指定欄位的值或者指定的方法
  Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;

  //返回屬性相應的類型
  Class<?> getType();
}

invoker介面的實現如下圖:

​ GetFieldInvoker/SetFieldInvoker通過field欄位封裝了FIeld對象,兩者的invoke()方法是通過調用Field.get()/set()方法實現的。MethodInvoker通過method欄位封裝了對應的Method對象,其invoke()方法是通過調用Method.invoke()方法實現的。

4. ReflectorFactory

ReflectorFactory介面主要實現了對Reflector對象的創建和緩存,介面定義如下:


public interface ReflectorFactory {

  //檢查該ReflectorFactory對象是否會緩存Reflector對象
  boolean isClassCacheEnabled();

  //設置是否緩存Reflector對象
  void setClassCacheEnabled(boolean classCacheEnabled);

  //創建指定對象的Reflector對象
  Reflector findForClass(Class<?> type);
}

MyBatis對ReflectorFactory介面只提供了DefaultReflectorFactory這一個實現類,他與Reflector關係如圖:

DefaultReflectorFactory的欄位含義如下:

//該欄位決定是否對Reflector對象的緩存
private boolean classCacheEnabled = true;
//使用ConcurrentHashMap集合實現對Reflector對象的緩存
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();

DefaultReflectorFactory提供findForClass()方法實現為指定Class創建Reflector對象,並將Reflector對象緩存到reflectorMap集合中,具體代碼如下:

public Reflector findForClass(Class<?> type) {
  if (classCacheEnabled) { //檢測是否開啟緩存
   //cas放入緩存
   return reflectorMap.computeIfAbsent(type, Reflector::new);
  } else {
   //未開啟緩存則直接創建
   return new Reflector(type);
  }
}

此外除了使用MyBatis提供的DefaultReflectorFactory實現,們還可以在mybatis-config.xml中配置自定義的ReflectorFactory實現類,從而實現上面功能的拓展。(後面會在介紹MyBatis初始化流程時,會提到拓展點。)

5. TypeParameterResolver

1. Type知識準備

在分析TypeParameterResolver之前我們先來看下Type介面,Type是所有類型的父介面,他有四個介面和一個實現類,類圖如圖所示:

Class

Class:表示原始類型類型,Class對象表示JVM中一個類或者介面,每個Java類在JVM都表現為一個Class對象。在程式中可以通過 ”xxx.class“,”對象.getClass()"或者是Class.forName("類名")來獲取Class對象。數組也被映射為Class對象,所有元素類型相同切維數相同的數組都共用一個Class對象。

ParameterizedType

ParameterizedType:表示參數化類型,例如List< String >,Map<Integer,String>,Service< User >這種帶有泛型的類型。

GenericArrayType

GenericArrayType:表示的是數組類型且組成元素是ParameterizedType或者TypeVariable。例如List< String >[]或者T[]

TypeVariable

TypeVariable:表示的是類型變數,它用來反映在JVM編譯該泛型前的信息。例如在LIst中的T就是類型變數,它在編譯的時候需要被轉化成一個具體的類型才能正常使用。

WildcardType

WildcardType:表示通配符類型,例如:? extends Number? extends Integer

2. 方法解析

上面介紹了Type介面的基礎知識,我們回來繼續分析TypeParameterResolver。在對Reflector的分析中我們看到了TypeParameterResolver的身影,他是一個工具類提供一些列靜態方法來解析指定類中的欄位、方法返回值或者方法參數的類型。TypeParameterResolver中各個方法之間的調用大致如下:【其中有一些遞歸調用沒有體現出來,在後面的分析中會進行強調。】

TypeParameterResolver中通過resolveFieldTypes()方法、resolveReturnTypes()方法、resolveParamTypes()方法分別解析欄位類型、方法返回值類型和方法參數列表中各個參數的類型。這三個方法類似。我們就以resolveFieldTypes()來分析。TypeParameterResolve.resolveFieldTypes()方法實現代碼如下:

public static Type resolveFieldType(Field field, Type srcType) {
  //獲取欄位的聲明類型
  Type fieldType = field.getGenericType();
  //獲取欄位定義所在類的Class對象
  Class<?> declaringClass = field.getDeclaringClass();
  //調用resolveType()方法做後續處理
  return resolveType(fieldType, srcType, declaringClass);
}

上述三個解析方法都會調用resolveType()會根據第一個參數Type,即方法、返回值或者方法返回參數的類型,選擇合適的方法解析。resolveType()的第二個參數表示查找該欄位、返回值或者方法參數的起始位置。第三個參數則表示該欄位、方法所在的類。

TypeParameterResolve.resolveType()方法代碼如下:

private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
  if (type instanceof TypeVariable) {
    //解析 TypeVariable 類型
    return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
  } else if (type instanceof ParameterizedType) {
    //解析  ParameterizedType 類型
    return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
  } else if (type instanceof GenericArrayType) {
    //解析  GenericArrayType 類型
    return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
  } else {
    return type;
  }
}

TypeParameterResolve.resolveParameterizedType()方法代碼如下:

private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class<?> declaringClass) {
  // 得到參數化類型中的原始類型對應的Class對象
  Class<?> rawType = (Class<?>) parameterizedType.getRawType();
  //返回參數化類型的類型變數
  Type[] typeArgs = parameterizedType.getActualTypeArguments();
  //用於保存解析後的結果
  Type[] args = new Type[typeArgs.length];
  for (int i = 0; i < typeArgs.length; i++) {
    if (typeArgs[i] instanceof TypeVariable) {
      //解析類型變數
      args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
    } else if (typeArgs[i] instanceof ParameterizedType) {
      //如果嵌套了ParameterizedType則調用resolveParameterizedType()方法進行處理
      args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
    } else if (typeArgs[i] instanceof WildcardType) {
      //如果嵌套了WildcardType則調用resolveWildcardType()方法進行處理
      args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
    } else {
      args[i] = typeArgs[i];
    }
  }
  //將解析結果封裝成TypeParameterResolver中定義的ParameterizedType實現並返回
  return new ParameterizedTypeImpl(rawType, null, args);
}

TypeParameterResolve.resolveTypeVar()方法負責解析TypeVariable。具體實現如下:


private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
  Type result;
  Class<?> clazz;
  if (srcType instanceof Class) {
    clazz = (Class<?>) srcType;
  } else if (srcType instanceof ParameterizedType) {
    ParameterizedType parameterizedType = (ParameterizedType) srcType;
    clazz = (Class<?>) parameterizedType.getRawType();
  } else {
    throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
  }

  if (clazz == declaringClass) {
    //獲取上界
    Type[] bounds = typeVar.getBounds();
    if (bounds.length > 0) {
      return bounds[0];
    }
    return Object.class;
  }
  //獲取聲明的父類類型
  Type superclass = clazz.getGenericSuperclass();
  //通過掃描父類進行後續解析 這個是遞歸的入口
  result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
  if (result != null) {
    return result;
  }
  // 獲取介面
  Type[] superInterfaces = clazz.getGenericInterfaces();
  for (Type superInterface : superInterfaces) {
    //通過掃描介面進行後續解析 邏輯同掃描父類
    result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
    if (result != null) {
      return result;
    }
  }
  //若在某個集成結構中解析成功則返回Object.class
  return Object.class;
}

scanSuperTypes()方法具體實現如下:

private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
  if (superclass instanceof ParameterizedType) {
    ParameterizedType parentAsType = (ParameterizedType) superclass;
    //獲取父類的原始類型
    Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
    TypeVariable<?>[] parentTypeVars = parentAsClass.getTypeParameters();
    if (srcType instanceof ParameterizedType) {
      //轉化父類參數化類型
      parentAsType = translateParentTypeVars((ParameterizedType) srcType, clazz, parentAsType);
    }
    if (declaringClass == parentAsClass) {
      for (int i = 0; i < parentTypeVars.length; i++) {
        if (typeVar.equals(parentTypeVars[i])) {
          return parentAsType.getActualTypeArguments()[i];
        }
      }
    }
    if (declaringClass.isAssignableFrom(parentAsClass)) {
      //繼續解析父類,知道解析到定義該欄位的類
      return resolveTypeVar(typeVar, parentAsType, declaringClass);
    }
  } else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class<?>) superclass)) {
    //聲明的父類不再含有類型變數且不是定義該欄位的類則繼續解析
    return resolveTypeVar(typeVar, superclass, declaringClass);
  }
  return null;
}

分析完TypeParameterResolver.resolveTypeVar()和resolveParameterizedType()兩個方法後,再來看resolveGenericArrayType()方法,該方法負責解析GenericArrayType類型的變數,它會根據數組元素的類型選擇合適的resolve *()方法進行解析,具體實現如下:

private static Type resolveGenericArrayType(GenericArrayType genericArrayType, Type srcType, Class<?> declaringClass) {
  //獲取數組元素的類型
  Type componentType = genericArrayType.getGenericComponentType();
  Type resolvedComponentType = null;
  //根據數組元素類型選擇合適的方法進行解析
  if (componentType instanceof TypeVariable) {
    resolvedComponentType = resolveTypeVar((TypeVariable<?>) componentType, srcType, declaringClass);
  } else if (componentType instanceof GenericArrayType) {
    //遞歸調用resolveGenericArrayType()方法
    resolvedComponentType = resolveGenericArrayType((GenericArrayType) componentType, srcType, declaringClass);
  } else if (componentType instanceof ParameterizedType) {
    resolvedComponentType = resolveParameterizedType((ParameterizedType) componentType, srcType, declaringClass);
  }
  //根絕解析後的數組項類型構造返回類型
  if (resolvedComponentType instanceof Class) {
    return Array.newInstance((Class<?>) resolvedComponentType, 0).getClass();
  } else {
    return new GenericArrayTypeImpl(resolvedComponentType);
  }
}

最後我們來分析一下TypeParameterResolver.resolveWildcardType()方法。該方法負責解析WildcardType類型的變數。首先解析WildcardType類型的上下界,然後通過解析後的結果構造WildcardTypeImpl對象返回。具體解析過程與上述resolve *()類似。

通過上面的方法分析我們知道,當存在複雜的繼承關係以及泛型類型時,TypeParameterResolver可以幫助我們解析欄位、方法和方法返回值的類型。這個是Reflector類的基礎。

MyBatis源碼中也提供了TypeParameterResolver相關的TypeParameterResolverTest這個測試類,可以從多方面測試TypeParameterResolver的功能。我們可以參考一下更好的瞭解TypeParameterResolver的功能。

6. ObjectFactory

MyBatis中有很多模塊會使用到ObjectFactory這個介面,該介面提供了多個crate()方法的重載,通過這些方法可以創建指定類型的對象。ObjectFactory介面定義如下:

public interface ObjectFactory {
  //設置配置信息
  default void setProperties(Properties properties) {
    // NOP
  }
  //通過無參構造器創建指定類的對象
  <T> T create(Class<T> type);
  //根據參數列表,從指定類型中選擇合適的構造器創建對象
  <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
  //檢測指定類型是否為集合類型,主要處理java.util.Collection及其子類
  <T> boolean isCollection(Class<T> type);
}

DefaultObjectFactory是MyBatis提供的ObjectFactory介面的唯一實現,它是一個反射工廠,其create()方法通過調用instantiateClass()方法實現。DefaultObjectFactory.instantiateClass()方法會根據傳入的參數列表選擇合適的構造函數實例化對象,具體實現如下:

private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  try {
    Constructor<T> constructor;
    //通過無參構造函數創建對象
    if (constructorArgTypes == null || constructorArgs == null) {
      constructor = type.getDeclaredConstructor();
      try {
        return constructor.newInstance();
      } catch (IllegalAccessException e) {
        if (Reflector.canControlMemberAccessible()) {
          constructor.setAccessible(true);
          return constructor.newInstance();
        } else {
          throw e;
        }
      }
    }
    //根據指定的參數列好查找構造函數,並實例化對象
    constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));
    try {
      return constructor.newInstance(constructorArgs.toArray(new Object[0]));
    } catch (IllegalAccessException e) {
      if (Reflector.canControlMemberAccessible()) {
        constructor.setAccessible(true);
        return constructor.newInstance(constructorArgs.toArray(new Object[0]));
      } else {
        throw e;
      }
    }
  } catch (Exception e) {
    String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)
        .stream().map(Class::getSimpleName).collect(Collectors.joining(","));
    String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)
        .stream().map(String::valueOf).collect(Collectors.joining(","));
    throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
  }
}

除了使用MyBatis提供的DefaultObjectFactory實現,我們還可以在mybatis-config.xml配置文件中指定自定義的ObjectFactory介面實現類,從而實現功能上的拓展。

7. Property工具集

下麵我們介紹反射模塊使用到的三個屬性工具類,分別是PropertyTokenizerPropertyNamerPropertyCopier

PropertyTokenizer

PropertyTokenizer中各個欄位含義如下:

//當前表達式的名稱
private String name;
//當前表達式的索引名
private final String indexedName;
//索引下標
private String index;
//子表達式
private final String children;

在PropertyTokenizer的構造函數中會對傳入的表達式進行分析,並初始化上面的屬性欄位,實現如下:

public PropertyTokenizer(String fullname) {
  //查找"."的位置
  int delim = fullname.indexOf('.');
  if (delim > -1) {
    //初始化name
    name = fullname.substring(0, delim);
    //初始化children
    children = fullname.substring(delim + 1);
  } else {
    name = fullname;
    children = null;
  }
  //初始化indexedName
  indexedName = name;
  delim = name.indexOf('[');
  if (delim > -1) {
    //初始化index
    index = name.substring(delim + 1, name.length() - 1);
    name = name.substring(0, delim);
  }
}

PropertyTokenizer繼承了Iterator介面,它可以迭代處理嵌套多層表達式。PropertyTokenizer.next()會創建新的PropertyTokenizer對象並解析children欄位記錄的子表達式。

PropertyNamer

PropertyNamer是另一個工具類,將提供了下列靜態方法幫助完成方法名到屬性名的轉換,以及多種檢測操作。

public final class PropertyNamer {

  private PropertyNamer() {
    // Prevent Instantiation of Static Class
  }

  //該方法會將方法名轉換成屬性
  public static String methodToProperty(String name) {
    if (name.startsWith("is")) {
      name = name.substring(2);
    } else if (name.startsWith("get") || name.startsWith("set")) {
      name = name.substring(3);
    } else {
      throw new ReflectionException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");
    }

    if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
      name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
    }

    return name;
  }

  // 檢測是否是對應屬性名
  public static boolean isProperty(String name) {
    return isGetter(name) || isSetter(name);
  }

  //檢測是否為getter方法
  public static boolean isGetter(String name) {
    return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
  }

  //檢測是否為setter方法
  public static boolean isSetter(String name) {
    return name.startsWith("set") && name.length() > 3;
  }
}

PropertyCopier

PropertyCopier是一個屬性拷貝的工具類。其核心方法是copyBeanProperties()方法,主要實現相同類型的兩個對象之間的屬性拷貝,具體實現如下:

public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) {
  Class<?> parent = type;
  while (parent != null) {
    final Field[] fields = parent.getDeclaredFields();
    for (Field field : fields) {
      try {
        try {
          field.set(destinationBean, field.get(sourceBean));
        } catch (IllegalAccessException e) {
          if (Reflector.canControlMemberAccessible()) {
            field.setAccessible(true);
            //將sourceBean對象中的屬性設置到destinationBean對象中
            field.set(destinationBean, field.get(sourceBean));
          } else {
            throw e;
          }
        }
      } catch (Exception e) {
        // Nothing useful to do, will only fail on final fields, which will be ignored.
      }
    }
    // 繼續拷貝父類中的定義的欄位
    parent = parent.getSuperclass();
  }
}

8. MetaClass

MetaClass通過Reflector和PropertyTokenizer組合使用,實現了對複雜的屬性表達式的解析,並實現了獲取指定屬性信息的功能。MetaClass中各個欄位的含義如下:

//用於緩存Reflector對象
private final ReflectorFactory reflectorFactory;
//在創建MetaClasyos時會指定一個類,該Reflector對象會用於記錄該類相關的元數據
private final Reflector reflector;

MetaClass的構造函數會為指定的Class創建對應的Reflector對象,並用其初始化MetaClass.reflector欄位,具體代碼如下:

//MetaClass的構造方法是使用private修飾的
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
  this.reflectorFactory = reflectorFactory;
  //創建Reflector對象
  this.reflector = reflectorFactory.findForClass(type);
}

//使用靜態方法創建MetaClass對象
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
  return new MetaClass(type, reflectorFactory);
}

MetaClass中比較重要的是findProperty()方法,他是通過MetaClass.buildProperty()方法實現的,而buildProperty()方法會通過PropertyTokenizer解析更複雜的屬性表達式,具體實現如下:

public String findProperty(String name) {
  //委托給buildProperty()方法實現
  StringBuilder prop = buildProperty(name, new StringBuilder());
  return prop.length() > 0 ? prop.toString() : null;
}
private StringBuilder buildProperty(String name, StringBuilder builder) {
    //解析屬性表達式
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) { //是否有子表達式
      //查找PropertyTokenizer.name對應的屬性
      String propertyName = reflector.findPropertyName(prop.getName());
      if (propertyName != null) {
        //追加屬性名
        builder.append(propertyName);
        builder.append(".");
        //為該屬性創建MataClass對象
        MetaClass metaProp = metaClassForProperty(propertyName);
        //遞歸解析PropertyTokenizer.children欄位,並將解析結果添加到builder中保存
        metaProp.buildProperty(prop.getChildren(), builder);
      }
    } else {
      //遞歸出口
      String propertyName = reflector.findPropertyName(name);
      if (propertyName != null) {
        builder.append(propertyName);
      }
    }
    return builder;
 }
 public MetaClass metaClassForProperty(String name) {
    //查找指定屬性對應的class
    Class<?> propType = reflector.getGetterType(name);
    //為改屬性創建對應的MetaClass對象
    return MetaClass.forClass(propType, reflectorFactory);
 }

註意:Meta.Class.findProperty()方法只查找"."導航的屬性,並沒有檢測下標。

MetaClass.hasGetter()方法和MetaClass.hasSetter()方法負責判斷屬性表達式所表示的屬性是否有對應的屬性,這兩個方法邏輯類似,我們以hasGetter()方法為例子進行分析,這兩個類方法實現最終都會查找Reflector.getMethods集合或Reflector.setMethods集合。根據前面介紹的Reflector.addFields()方法,當欄位沒有對應的getter/setter方法時會添加相應的getFieldInvoker/setFieldInvoker對象,所以Reflector有許可權訪問指定欄位的時候這兩個方法的行為並不像其方法所暗示的那樣只直接判斷屬性的getter/setter方法,我們來看看MetaClass.hasGetter()方法的實現代碼:

public boolean hasGetter(String name) {
  //解析屬性表達式
  PropertyTokenizer prop = new PropertyTokenizer(name);
  //存在待處理的子表達式
  if (prop.hasNext()) {
    //PropertyTokenizer.name指定的屬性有getter方法 才能處理子表達式
    if (reflector.hasGetter(prop.getName())) {
      //metaClassForProperty(PropertyTokenizer)是上面metaClassForProperty(String)的重載 但是邏輯有很大差異
      MetaClass metaProp = metaClassForProperty(prop);
      //遞歸入口
      return metaProp.hasGetter(prop.getChildren());
    } else {
      //遞歸出口
      return false;
    }
  } else {
    //遞歸出口
    return reflector.hasGetter(prop.getName());
  }
}

MetaClass.metaClassForProperty(PropertyTokenizer)方法底層會調用MetaClass.getGetterType(PropertyTokenizer)方法,針對PropertyTokenizer中是否包含索引信息進一步處理,代碼如下:

private MetaClass metaClassForProperty(PropertyTokenizer prop) {
  //獲取表達式標識的屬性的類型
  Class<?> propType = getGetterType(prop);
  return MetaClass.forClass(propType, reflectorFactory);
}

private Class<?> getGetterType(PropertyTokenizer prop) {
  //獲取屬性類型
  Class<?> type = reflector.getGetterType(prop.getName());
  //該表達式中是否試用"[]"指定了下標,切是Collect子類
  if (prop.getIndex() != null && Collection.class.isAssignableFrom(type)) {
    //通過TypeParameterResolver工具類解析屬性的類型
    Type returnType = getGenericGetterType(prop.getName());
    //針對ParameterizedType進行處理,既針對泛型類型進行處理
    if (returnType instanceof ParameterizedType) {
      //獲取實際的類型參數
      Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
      if (actualTypeArguments != null && actualTypeArguments.length == 1) {
        //泛型的類型
        returnType = actualTypeArguments[0];
        if (returnType instanceof Class) {
          type = (Class<?>) returnType;
        } else if (returnType instanceof ParameterizedType) {
          type = (Class<?>) ((ParameterizedType) returnType).getRawType();
        }
      }
    }
  }
  return type;
}

private Type getGenericGetterType(String propertyName) {
  try {
    //根據Reflector.getMethods集合中記錄的invoker實現類的類型,決定解析getter方法返回值類型還是解析欄位類型
    Invoker invoker = reflector.getGetInvoker(propertyName);
    if (invoker instanceof MethodInvoker) {
      Field _method = MethodInvoker.class.getDeclaredField("method");
      _method.setAccessible(true);
      Method method = (Method) _method.get(invoker);
      return TypeParameterResolver.resolveReturnType(method, reflector.getType());
    } else if (invoker instanceof GetFieldInvoker) {
      Field _field = GetFieldInvoker.class.getDeclaredField("field");
      _field.setAccessible(true);
      Field field = (Field) _field.get(invoker);
      return TypeParameterResolver.resolveFieldType(field, reflector.getType());
    }
  } catch (NoSuchFieldException | IllegalAccessException ignored) {
  }
  return null;
}

MetaClass中的其他get *()方法比較簡單,大多數直接依賴Reflector的對應方法的實現的。通過對MetaClass的分析,我們瞭解了findProperty()hasGetter()hasSetter()等方法的實現原理。

9. ObjectWrapper

ObjectWrapper顧名思義就是對象的包裝類,對對象級別的元信息進行處理。ObjectWrapper介面是對對象的包裝,抽象了對象的屬性信息,它定義了一系列的查詢對屬性信息的方法,以及更新屬性的方法。

ObjectWrapper介面的定義如下:

public interface ObjectWrapper {
  //如果是普通bean調用getter方法 如果是集合 則獲取指定key或者下標對應的value值
  Object get(PropertyTokenizer prop);
  //如果是普通bean調用setter方法 如果是集合 則設置指定key或者下標對應的value值
  void set(PropertyTokenizer prop, Object value);
  //查找屬性表達式指定的屬性,第二個參數標識是否忽視屬性表達式的下劃線
  String findProperty(String name, boolean useCamelCaseMapping);

  //查找可讀屬性的名稱集合
  String[] getGetterNames();
  //查找可寫屬性的名稱集合
  String[] getSetterNames();

  //解析表達式指定屬性的setter方法的參數類型
  Class<?> getSetterType(String name);
  //解析表達式指定屬性的getter方法的參數類型
  Class<?> getGetterType(String name);

  //判斷屬性表達式指定屬性是否有getter方法
  boolean hasSetter(String name);
  //判斷屬性表達式指定屬性是否有setter方法
  boolean hasGetter(String name);

  //為屬性表達式指定的屬性創建相應的MetaObject對象
  MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);

  //封裝的對象是否為Collect類型
  boolean isCollection();

  //調用Collection對象的add()方法
  void add(Object element);
  //調用Collection對象的addAll()方法
  <E> void addAll(List<E> element);
}

ObjectWrapperFactory負責創建ObjectWrapper對象,關係如圖所示:

DefaultObjectWrapperFactory實現了ObjectWrapperFactory介面,但它實現的getWrapperFor()方法始終拋出異常,hasWrapperFor()方法始終返回false,所以該實現方法實際上是不可用的。但是與ObjectFactory類似,我們可以在mybatis-config.xml配置自定義的ObjectWrapperFactory實現類進行拓展。

BaseWrapper是一個實現了ObjectWrapper介面的抽象類,其中封裝了MetaObject對象,並提供了三個常用的方法供其子類使用。如圖所示:

BaseWrapper.resolveCollection()方法會調用MetaObject.getValue()方法,它會解析屬性表達式並獲取指定的屬性,MetaObject.getValue()方法的實現在下麵介紹。

BaseWrapper.getCollectionValue()方法和setCollectionValue()方法會解析屬性表達式的索引信息,然後獲取或設置對應項。這兩個方法類似。在這裡我們只分析一下getCollectionValue()方法。

protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
  if (collection instanceof Map) {
    //如果是map類型則index為key
    return ((Map) collection).get(prop.getIndex());
  } else {
    //如果是其他類型則index為下標
    int i = Integer.parseInt(prop.getIndex());
    if (collection instanceof List) {
      return ((List) collection).get(i);
    } else if (collection instanceof Object[]) {
      return ((Object[]) collection)[i];
    } else if (collection instanceof char[]) {
      return ((char[]) collection)[i];
    } else if (collection instanceof boolean[]) {
      return ((boolean[]) collection)[i];
    } else if (collection instanceof byte[]) {
      return ((byte[]) collection)[i];
    } else if (collection instanceof double[]) {
      return ((double[]) collection)[i];
    } else if (collection instanceof float[]) {
      return ((float[]) collection)[i];
    } else if (collection instanceof int[]) {
      return ((int[]) collection)[i];
    } else if (collection instanceof long[]) {
      return ((long[]) collection)[i];
    } else if (collection instanceof short[]) {
      return ((short[]) collection)[i];
    } else {
      throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
    }
  }
}

BeanWrapper繼承了BaseWrapper抽象類,其中封裝了一個JavaBean對象以及該JavaBean相應的MetaClass對象,當然還有從BaseWrapper繼承下來的、該JavaBean對象相應的MetaObject對象。

BeanWrapper.get()方法和set()方法會根據指定的屬性表達式,獲取或者設置相應的屬性值,兩者邏輯相似,這裡我們以get()方法分析,具體代碼如下:

public Object get(PropertyTokenizer prop) {
  //存在索引信息 則表示屬性表達式中的name部分為集合類型
  if (prop.getIndex() != null) {
    //通過MetaObject.getValue()方法獲取object對象中指定集合屬性
    Object collection = resolveCollection(prop, object);
    //獲取集合元素
    return getCollectionValue(prop, collection);
  } else {
    //不存在索引信息 則name部分為普通對象,查找並調用invoker相關方法獲取屬性
    return getBeanProperty(prop, object);
  }
}
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
    try {
      //根據屬性名稱查找Reflector.getMethods集合中相應的getFieldInvoker或者MethodInvoker
      Invoker method = metaClass.getGetInvoker(prop.getName());
      try {
        //獲取屬性值
        return method.invoke(object, NO_ARGUMENTS);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (RuntimeException e) {
      throw e;
    } catch (Throwable t) {
      throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ".  Cause: " + t.toString(), t);
    }
  }

CollectionWrapper實現了ObjectWrapper介面,其中封裝了Collection< Object >類型的對象,但是它大部分實現方法都會拋出UnsupportedOperationException異常。

MapWrapper是BaseWrapper的另一個實現類,其中封裝了Map<String,Object>類型對象,我們瞭解了MetaObject和BeanWrapper實現後,輕而易舉就能看懂MapWrapper的實現代碼。

10. MetaObject

MetaObject提供了獲取/設置對象中指定的屬性值、檢測getter/setter等常用功能,但是ObjectWrapper只是這些功能的最後一站,我們省略了對屬性表達式解析過程的介紹,而該解析過程就是在MetaObject中實現的。

MetaObject中欄位的含義如下:

//原始JavaBean對象
private final Object originalObject;
//封裝了originalObject對象
private final ObjectWrapper objectWrapper;
//負責實例化originalObject的工廠對象
private final ObjectFactory objectFactory;
//負責創建ObjectWrapper的工廠對象
private final ObjectWrapperFactory objectWrapperFactory;
//用於創建並緩存Reflector對象的工廠對象
private final ReflectorFactory reflectorFactory;

MetaObject的構造方法會根據傳入的原始對象的類型以及ObjectFactory工廠的實現創建相應的ObjectWrapper對象,代碼如下:

private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
  //初始化上述欄位
  this.originalObject = object;
  this.objectFactory = objectFactory;
  this.objectWrapperFactory = objectWrapperFactory;
  this.reflectorFactory = reflectorFactory;

  //若原始對象已經是ObjectWrapper對象則直接使用
  if (object instanceof ObjectWrapper) {
    this.objectWrapper = (ObjectWrapper) object;
  } else if (objectWrapperFactory.hasWrapperFor(object)) {
    //若objectWrapperFactory能夠為該原始兌現創建對應的ObjectWrapper對象則由優先使用objectWrapperFactory,
    //而DefaultObjectWrapperFactory.hasWrapperFor()始終返回false,用戶可以自定義ObjectWrapperFactory實現進行拓展
    this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
  } else if (object instanceof Map) {
    //若原始對象是map類型 則創建MapWrapper對象
    this.objectWrapper = new MapWrapper(this, (Map) object);
  } else if (object instanceof Collection) {
    //若原始對象是Collection類型 則創建CollectionWrapper對象
    this.objectWrapper = new CollectionWrapper(this, (Collection) object);
  } else {
    //若對象是普通的JavaBean對象,則創建BeanWrapper對象
    this.objectWrapper = new BeanWrapper(this, object);
  }
}
//Meta的構造方法是private的 只能靠forObject()這個靜態方法來創建MetaObject對象
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    if (object == null) {
      //若原始對象為空 則統一返回SystemMetaObject.NULL_META_OBJECT這個靜態對象
      return SystemMetaObject.NULL_META_OBJECT;
    } else {
      return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }
  }

MetaObject和ObjectWrapper中關於類級別的方法,例如hasGetter()、hasSetter()、findProperty()等方法,都是直接調用MetaClass的對應方法實現的。其他方法都是關於對象級別的方法,這些方法都是與ObjectWrapper配合實現,例如MetaObject.getValue()/setValue()方法,這裡以getValue()方法為例進行分析,具體代碼如下:

public Object getValue(String name) {
  //解析屬性表達式
  PropertyTokenizer prop = new PropertyTokenizer(name);
  if (prop.hasNext()) { //處理子表達式
    //根據PropertyTokenizer解析後制定的屬性 創建相應的MetaObject對象
    MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
    if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
      return null;
    } else {
      //遞歸處理子表達式
      return metaValue.getValue(prop.getChildren());
    }
  } else {
    //通過ObjectWrapper獲取指定的屬性值
    return objectWrapper.get(prop);
  }
}
public MetaObject metaObjectForProperty(String name) {
    //獲取指定的屬性
    Object value = getValue(name);
    //創建該屬性對象相應的MetaObject對象
    return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
  }

ObjectWrapper.instantiateProperty()方法實際上就是調用ObjectFactory介面的create()方法(預設實現是DefaultObjectFactory)創建對象並將其設置到所屬的對象中,這裡我們看下具體的實現代碼:

public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
  MetaObject metaValue;
  //獲取屬性相應的setter方法的參數類型
  Class<?> type = getSetterType(prop.getName());
  try {
    //通過反射的方式創建對象
    Object newObject = objectFactory.create(type);
    metaValue = MetaObject.forObject(newObject, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(), metaObject.getReflectorFactory());
    //將上面創建的屬性對象 設置到對應的屬性對象集合中
    set(prop, newObject);
  } catch (Exception e) {
    throw new ReflectionException("Cannot set value of property '" + name + "' because '" + name + "' is null and cannot be instantiated on instance of " + type.getName() + ". Cause:" + e.toString(), e);
  }
  return metaValue;
}

理解了MetaObject和BeanWrapper如何通過遞歸的方式處理屬性表達式指定的屬性值後,其他方法的實現原理就好理解了。例如getGetterType()、getSetterType()、hasGetter()、hasSetter()等方法,都是先遞歸處理屬性表達式,然後調用MetaClass相應的方法實現的。

本文由 Janker 創作,採用 CC BY 3.0 CN協議 進行許可。 可自由轉載、引用,但需署名作者且註明文章出處。如轉載至微信公眾號,請在文末添加作者公眾號二維碼。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 博客最近上了最新版本的FoundationJS、jQuery、Amaze、Modenizr四種框架的CDN 目的是自己用,想在博客裡面掛CDN(因為最新版本的FoundationJS沒有),自己有在學習怎麼用。 很多人不知道Fundation怎麼用,我會說的!!! FoundationJS6_CSS... ...
  • Node.js 提供一組類似 UNIX(POSIX)標準的文件操作API。 Node.js 文件系統(fs 模塊)模塊中的方法均有非同步和同步版本,例如讀取文件內容的函數有非同步的 fs.readFile() 和同步的 fs.readFileSync()。 非同步的方法函數最後一個參數為回調函數,回調函數 ...
  • jQuery+CSS3實現404背景動畫特效 效果: http://hovertree.com/texiao/jquery/74/ 源碼下載: http://hovertree.com/h/bjaf/ko0gcgw5.htm 效果圖如下: 代碼如下: 文章來自: "https://www.cnblo ...
  • 關於Element跟Node的區別,cilldren跟childNodes的區別很多朋友弄不清楚,本文試圖讓大家明白這幾個概念之間的區別。 Node(節點)是DOM層次結構中的任何類型的對象的通用名稱,Node有很多類型,如元素節點,屬性節點,文本節點,註釋節點等,通過NodeType區分,常見的有 ...
  • 裝飾模式的核心:被裝飾對象(最原始),裝飾對象(帶有裝飾功能的被裝飾對象)。 假設有一個被裝飾對象叫“ConcreteComponent ”,兩個裝飾對象叫“ConcreteDecoratorA”、“ConcreteDecoratorB”。裝飾是錦上添花的事情,ConcreteComponent 被 ...
  • etcd是什麼? etcd是一個用Go語言寫的,用於分散式系統下高性能的鍵值(K V)存儲、服務發現、負載均衡、、分散式鎖、配置管理等場景地應用,類似於Java的zookeeper。基於Raft協議,能保證數據的一致性。 官方地址 [etcd.io] https://etcd.io [github. ...
  • 什麼是池,我們在開發中經常會聽到有線程池啊,資料庫連接池等等。那麼到底什麼是池?其實很簡單,裝水的池子就叫水池嘛,用來裝線程的池子就叫線程池(廢話),就是我們把創建好的N個線程都放在一個池子裡面,如果有需要,我們就去取,不用額外的再去手動創建了為什麼要用線程池按照正常的想法是,我們需要一個線程,就去... ...
  • springboot的優質開發體驗,能夠更快速的搭建生產環境的項目。這篇文章就是一個超級簡單的入門springboot項目。包含了一些簡單的理論和超簡單Controller層 【工具】 IDEA 全稱IntelliJ IDEA SpringBoot 【創建項目】打開IDEA編輯器,新建項目,然後選擇 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...