Groovy核心類原始碼講解(上)

joytoy發表於2021-09-09

上一篇文章我們重點分析了android-gradle-plugin的幾個核心類以及透過UML類圖瞭解了這些核心類的一個相互依賴關係,不管是什麼型別的Plugin其實還是基於gradle這個程式設計框架,不同的Plugin只是為了實現特定的功能而已,而gradle這個框架的核心類呢,大家可以參考學習我的。今天我們重點來帶領大家學習一下gradle的底層基礎groovy的核心類,至於gradle與groovy的關係,與java和android的關係完全一樣,下面,我們就來看一下groovy中的一些核心類的原始碼。讓大家對gradle有一個更加深入的瞭解。
首先,我們來看一下groovy原始碼中有那幾個核心的包,如下圖:
圖片描述
所有的groovy程式碼都在groovy-all-2.4.11這個是jar包中,這個jar包中最重要的兩個包分別是:groovy和org,而groovy中重點存的就是一些groovy對json,xml等處理的類,我們下回再詳細講解這個包中的核心類,今天我們重點來分析一下org這個包中的核心類。org包中的所以類,我這裡就不再為大家截圖了,大家自行檢視jar包中的程式碼即可,下面我們來講解我們重點要講解的org/codehaus/groovy/runtime包中的核心類,其它包中的類也是一些輔助類,這個包中有如下幾個類是我們要重點分析的,他們的類UML圖如下所示:
圖片描述
下面我來為大家解釋一下這個類圖,可以看到核心的一個父類就是DefaultGroovyMethodsSupport,而其它的類都是他的子類,大家可以看到子類有一個共同的特點都是:XXXGroovyMethods,那我們為什麼說這幾個類是核心類呢,瞭解過groovy的同學都應該知道,groovy是java的擴充套件,那groovy擴充套件了那些東西呢,除了我們還沒有講的groovy包中的一些類,另一部分的擴充套件就是我們圖中的這些XXXGroovyMethods類中的方法,舉個例子,大家來看下面這段程式碼:

String content = new File('config.txt').text
println content

這兩句程式碼的作用也非常的明顯,就是獲取到檔案中的內容,並列印出來,在java中,我們要建立檔案類,然後透過建立一個流來讀取出檔案內容,而要關閉檔案流。非常的累贅。而在groovy中則非常的清爽,下面我們看一下groovy是如何實現這個text方法的,程式碼如下:

//最終的實現就在這裡,這個方法是在IOGroovyMethods類中
public static String getText(BufferedReader reader) throws IOException {
    StringBuilder answer = new StringBuilder();
    char[] charBuffer = new char[8192];

    try {
      int nbCharRead;
      while((nbCharRead = reader.read(charBuffer)) != -1) {
        answer.append(charBuffer, 0, nbCharRead);
      }

      Reader temp = reader;
      reader = null;
      temp.close();
    } finally {
      closeWithWarning(reader);
    }

    return answer.toString();
  }

透過這段程式碼可以看到,他底層的實現與我們Java原來的實現完全一樣,只是groovy替我們完成了封裝使得我們可以直接透過getText()方法來得到整個文字檔案的內容。從這個方法,我們就可以看出groovy對java所作的擴充套件有多麼的強大。

這也就是我們為什麼要重點看這幾個類,大家要把這幾個類都瞭解的話就基本可以知道groovy對java做了那些擴充套件,

下面我們就分別來看一下這幾個類是為java中的類做了那些擴充套件,我們首先來看一下DefaultGroovyMethods這個方法做了那些擴充套件,首先大家
要知道的是,這個類是對任何java中的類的一個擴充套件,也就是java中的任何一個類,在groovy中都是可以使用DefaultGroovyMethods中的方法的,下面我們來看一些比較常用的方法:

public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
  private static final Logger LOG = Logger.getLogger(DefaultGroovyMethods.class.getName());
  private static final Integer ONE = Integer.valueOf(1);
  private static final BigInteger BI_INT_MAX = BigInteger.valueOf(2147483647L);
  private static final BigInteger BI_INT_MIN = BigInteger.valueOf(-2147483648L);
  private static final BigInteger BI_LONG_MAX = BigInteger.valueOf(9223372036854775807L);
  private static final BigInteger BI_LONG_MIN = BigInteger.valueOf(-9223372036854775808L);
  public static final Class[] additionals = new Class[]{NumberNumberPlus.class, NumberNumberMultiply.class, NumberNumberMinus.class, NumberNumberDiv.class, ObjectArrayGetAtMetaMethod.class, ObjectArrayPutAtMetaMethod.class, BooleanArrayGetAtMetaMethod.class, BooleanArrayPutAtMetaMethod.class, ByteArrayGetAtMetaMethod.class, ByteArrayPutAtMetaMethod.class, CharacterArrayGetAtMetaMethod.class, CharacterArrayPutAtMetaMethod.class, ShortArrayGetAtMetaMethod.class, ShortArrayPutAtMetaMethod.class, IntegerArrayGetAtMetaMethod.class, IntegerArrayPutAtMetaMethod.class, LongArrayGetAtMetaMethod.class, LongArrayPutAtMetaMethod.class, FloatArrayGetAtMetaMethod.class, FloatArrayPutAtMetaMethod.class, DoubleArrayGetAtMetaMethod.class, DoubleArrayPutAtMetaMethod.class};
  public static final Class[] DGM_LIKE_CLASSES = new Class[]{DefaultGroovyMethods.class, DateGroovyMethods.class, EncodingGroovyMethods.class, IOGroovyMethods.class, ProcessGroovyMethods.class, ResourceGroovyMethods.class, SocketGroovyMethods.class, StringGroovyMethods.class};
  private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
  private static final NumberAwareComparator COMPARABLE_NUMBER_AWARE_COMPARATOR = new NumberAwareComparator();

  public DefaultGroovyMethods() {
  }

  //用來直接判斷other物件是否與呼叫物件相等
  public static boolean is(Object self, Object other) {
    return self == other;
  }
  //直接判斷迭代器中的物件是否是唯一的
  public static  Iterator unique(Iterator self) {
    return toList((Iterable)unique(toList(self))).listIterator();
  }

  public static  Collection unique(Collection self) {
    return unique(self, true);
  }
  //任意物件都都可以使用each方法來進行遍歷,當然如果是不可迭代物件,會報錯
 public static  T each(T self, Closure closure) {
    each(InvokerHelper.asIterator(self), closure);
    return self;
  }

  public static  T eachWithIndex(T self, Closure closure) {
    Object[] args = new Object[2];
    int counter = 0;
    Iterator iter = InvokerHelper.asIterator(self);

    while(iter.hasNext()) {
      args[0] = iter.next();
      args[1] = Integer.valueOf(counter++);
      closure.call(args);
    }

    return self;
  }

這裡就不一一列出了,這個類是對有java物件做的一個擴充套件。

下面我們再來看StringGroovyMethods,從這個類的類名我們就可以看出,這個類是針對java中的String做的擴充套件,下面我們來看一下這個類中對Java中的String類做了那些擴充套件:

public class StringGroovyMethods extends DefaultGroovyMethodsSupport {
  static String lineSeparator = null;

  public StringGroovyMethods() {
  }

  public static String uncapitalize(CharSequence self) {
    String s = self.toString();
    return s != null && s.length() != 0?Character.toLowerCase(s.charAt(0)) + s.substring(1):s;
  }
  //將String首字母大寫,這個方法非常的常用,尤其在自定義Task時候
  public static String capitalize(CharSequence self) {
    return self.length() == 0?"":"" + Character.toUpperCase(self.charAt(0)) + self.subSequence(1, self.length());
  }
  //遍歷字串中的每個字元,並將對應的字元應用閉包
  public static  T eachLine(CharSequence self, @ClosureParams(value = FromString.class,options = {"String", "String,Integer"}) Closure closure) throws IOException {
    return eachLine((String)self.toString(), 0, closure);
  }

  public static  T eachLine(CharSequence self, int firstLine, @ClosureParams(value = FromString.class,options = {"String", "String,Integer"}) Closure closure) throws IOException {
    int count = firstLine;
    T result = null;

    for(Iterator var5 = readLines(self.toString()).iterator(); var5.hasNext(); ++count) {
      String line = (String)var5.next();
      result = DefaultGroovyMethods.callClosureForLine(closure, line, count);
    }

    return result;
  }

其它的方法,我們也不再列出,可見透過StringGroovyMethods方法的擴充套件,可以極大的方便我們的開發。

而ResourceGroovyMethods和IOGroovyMethods則很明顯是對檔案IO流的擴充套件。一些常用的方法如下:

//獲取檔案中的位元組碼 
 public static byte[] getBytes(File file) throws IOException {
    return IOGroovyMethods.getBytes(new FileInputStream(file));
  }

 public static byte[] getBytes(InputStream is) throws IOException {
    ByteArrayOutputStream answer = new ByteArrayOutputStream();
    byte[] byteBuffer = new byte[8192];

    int nbByteRead;
    try {
      while((nbByteRead = is.read(byteBuffer)) != -1) {
        answer.write(byteBuffer, 0, nbByteRead);
      }
    } finally {
      closeWithWarning(is);
    }

    return answer.toByteArray();
  }

//獲取檔案中的每一行內容,並對其應用閉包
public static  T eachLine(Reader self, @ClosureParams(value = FromString.class,options = {"String", "String,Integer"}) Closure closure) throws IOException {
    return eachLine((Reader)self, 1, closure);
  }

  public static  T eachLine(Reader self, int firstLine, @ClosureParams(value = FromString.class,options = {"String", "String,Integer"}) Closure closure) throws IOException {
    int count = firstLine;
    T result = null;
    BufferedReader br;
    if(self instanceof BufferedReader) {
      br = (BufferedReader)self;
    } else {
      br = new BufferedReader(self);
    }

    try {
      while(true) {
        String line = br.readLine();
        if(line == null) {
          Reader temp = self;
          self = null;
          temp.close();
          Object var7 = result;
          return var7;
        }

        result = DefaultGroovyMethods.callClosureForLine(closure, line, count);
        ++count;
      }
    } finally {
      closeWithWarning(self);
      closeWithWarning(br);
    }
  }
//獲取每行檔案內容並返回撥用者
public static String readLine(Reader self) throws IOException {
    if(self instanceof BufferedReader) {
      BufferedReader br = (BufferedReader)self;
      return br.readLine();
    } else {
      return self.markSupported()?readLineFromReaderWithMark(self):readLineFromReaderWithoutMark(self);
    }
  }
//對每個位元組使用閉包,與eachLine作用類似
public static void eachByte(InputStream is, @ClosureParams(value = SimpleType.class,options = {"byte"}) Closure closure) throws IOException {
    try {
      while(true) {
        int b = is.read();
        if(b == -1) {
          InputStream temp = is;
          is = null;
          temp.close();
          return;
        }

        closure.call(Byte.valueOf((byte)b));
      }
    } finally {
      closeWithWarning(is);
    }
  }

這是我舉出來的這兩個類中的常用方法,可以看到,有了groovy對IO流的擴充套件,我們可以直接對檔案,流進行更方便的操作。

最後一個比較重要的就是我們的EncodingGroovyMethods,這個類我們透過名字也可以看出, 是與編解碼相關的擴充套件,程式碼如下:

public class EncodingGroovyMethods {
  private static final char[] T_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();
  private static final String CHUNK_SEPARATOR = "rn";

  public EncodingGroovyMethods() {
  }

  public static Writable encodeBase64(Byte[] data, boolean chunked) {
    return encodeBase64(DefaultTypeTransformation.convertToByteArray(data), chunked);
  }

  public static Writable encodeBase64(Byte[] data) {
    return encodeBase64(DefaultTypeTransformation.convertToByteArray(data), false);
  }

  public static Writable encodeBase64(final byte[] data, final boolean chunked) {
    return new Writable() {
      public Writer writeTo(Writer writer) throws IOException {
        int charCount = 0;
        int dLimit = data.length / 3 * 3;

        int d;
        for(d = 0; d != dLimit; d += 3) {
          int dx = (data[d] & 255) > 18]);
          writer.write(EncodingGroovyMethods.T_TABLE[dx >> 12 & 63]);
          writer.write(EncodingGroovyMethods.T_TABLE[dx >> 6 & 63]);
          writer.write(EncodingGroovyMethods.T_TABLE[dx & 63]);
          if(chunked) {
            ++charCount;
            if(charCount == 19) {
              writer.write("rn");
              charCount = 0;
            }
          }
        }

        if(dLimit != data.length) {
          d = (data[dLimit] & 255) > 18]);
          writer.write(EncodingGroovyMethods.T_TABLE[d >> 12 & 63]);
          writer.write(dLimit + 1 > 6 & 63]:61);
          writer.write(61);
          if(chunked && charCount != 0) {
            writer.write("rn");
          }
        }

        return writer;
      }

      public String toString() {
        StringWriter buffer = new StringWriter();

        try {
          this.writeTo(buffer);
        } catch (IOException var3) {
          throw new StringWriterIOException(var3);
        }

        return buffer.toString();
      }
    };
  }

  public static Writable encodeBase64(byte[] data) {
    return encodeBase64(data, false);
  }

  public static byte[] decodeBase64(String value) {
    int byteShift = 4;
    int tmp = 0;
    boolean done = false;
    StringBuilder buffer = new StringBuilder();

    for(int i = 0; i != value.length(); ++i) {
      char c = value.charAt(i);
      int sixBit = c > byteShift * 2 & 255));
        }
      } else if(sixBit == 64) {
        --byteShift;
        done = true;
      } else if(sixBit == 66) {
        throw new RuntimeException("bad character in base64 value");
      }

      if(byteShift == 0) {
        byteShift = 4;
      }
    }

    try {
      return buffer.toString().getBytes("ISO-8859-1");
    } catch (UnsupportedEncodingException var8) {
      throw new RuntimeException("Base 64 decode produced byte values > 255");
    }
  }

  public static Writable encodeHex(Byte[] data) {
    return encodeHex(DefaultTypeTransformation.convertToByteArray(data));
  }

  public static Writable encodeHex(final byte[] data) {
    return new Writable() {
      public Writer writeTo(Writer out) throws IOException {
        for(int i = 0; i 

這個類中的方法也很明顯,我們可以將String編碼為Base64碼,16進位制位元組碼,也可以反向解析。

雖然org包中的類也非常的多,但是大家要重點關注的就是這幾個類(除去還未講的groovy包下的類)。這些對應的擴充套件方法的使用與我們使用類中已經定義的普通方法完全一樣,不瞭解的同學可以透過我的去學習。

好,本節的內容就到這裡,本小節的內容相對簡單,主要就是帶大家熟悉了幾個重要的擴充套件類,那,這些擴充套件類中的方法是如何繫結到java中的對應物件上的呢,這部分內容就比較複雜了,我們會在下一篇文章中詳細為大家講解,感興趣的同學可以先去了解一下groovy中的超程式設計,否則不太好理解下一節內容。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4650/viewspace-2804562/,如需轉載,請註明出處,否則將追究法律責任。

相關文章