深刻探索 Gradle 自动化构建技术(6、Gradle 插件平台化框架 ByteX 探秘之旅)

前言

成为一名优秀的Android开发,须要一份完备的知识体系,在这里,让咱们一块儿成长为本身所想的那样~。

1、前置知识

一、函数式编程

1)、什么是函数式编程?

面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。现实世界中,数据和行为并存,而程序也是如此。java

2)为何要学习函数式编程?

  • 用函数(行为)对数据处理,是学习大数据的基石。
  • 好的效率(并发执行),
  • 完成一个功能使用更少的代码。
  • 对象转向面向函数编程的思想有必定难度,须要大量的练习

二、Java 1.8 Lambda 表达式

1)、什么是 Lambda 表达式?

Lambda 是一个匿名函数,即没有函数名的函数,它简化了匿名委托的使用,让代码更加简洁。python

2)、两个简单的 Lambda 表达式示例

//匿名内部类
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.print("hello toroot");
    }
};

//lambda
Runnable r2 = ()->System.out.print("hello toroot");

//匿名内部类
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return Long.compare(o1.length(),o2.length());
    }
});

//lambda
TreeSet<String> ts2 = new TreeSet<>((o1,o2)-> Long.compare(o1.length(),o2.length()));
复制代码

3)、Lambda 表达式语法

Lambda 表达式在 Java 语言中引入了一个新的语法元素和操做符。这个操做符为 “->” ,该操做符被称为 Lambda 操做符或剪头操做符。android

它将 Lambda 分为两个部分:git

  • 左侧:指定了 Lambda 表达式须要的全部参数
  • 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能

4)、Lambda 表达式语法格式

  • 一、无参数,无返回值: () -> System.out.println("Hello Lambda!");
  • 二、有一个参数,而且无返回值: (x) -> System.out.println(x)
  • 三、若只有一个参数,小括号能够省略不写: x -> System.out.println(x)
  • 四、有两个以上的参数,有返回值,而且 Lambda 体中有多条语句:
Comparator<Integer> com = (x, y) -> {
			System.out.println("函数式接口");
			return Integer.compare(x, y);
		};
复制代码
  • 五、若 Lambda 体中只有一条语句, return 和 大括号 均可以省略不写: Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
  • 六、Lambda 表达式的参数列表的数据类型能够省略不写,由于 JVM 编译器能够经过上下文推断出数据类型,即“类型推断”: (Integer x, Integer y) -> Integer.compare(x, y);

5)、函数式接口

Lambda 表达式须要“函数式接口”的支持。函数式接口即 接口中只有一个抽象方法的接口,称为函数式接口。 可使用注解 @FunctionalInterface 修饰,它能够检查是不是函数式接口。函数式接口的使用示例以下所示:程序员

@FunctionalInterface
public interface MyFun {
    public double getValue();
}

@FunctionalInterface
public interface MyFun<T> {
    public T getValue(T t);
}

public static void main(String[] args) {
    String newStr = toUpperString((str)->str.toUpperCase(),"toroot");
    System.out.println(newStr);
}

public static String toUpperString(MyFun<String> mf,String str) {
    return mf.getValue(str);
}
复制代码

6)、Java 内置函数式接口

接口 参数 返回类型 示例
Predicate T boolean 这道题对了吗?
Consumer T void 输出一个值
Function<T,R> T R 得到 Person对象的名字
Supplier None T 工厂方法
UnaryOperator T T 逻辑非 (!)
BinaryOperator (T, T) T 求两个数的乘积 (*)

7)、方法引用

当要传递给 Lambda 体的操做,已经有实现的方法了,可使用方法引用。方法引用使用 操做符 “ ::” 将方法名和对象或类的名字分隔开来。它主要有以下 三种 使用状况 :github

  • 对象 :: 实例方法
  • 类 :: 静态方法
  • 类 :: 实例方法

8)、何时能够用 :: 方法引用(重点)

在咱们使用 Lambda 表达式的时候,”->” 右边部分是要执行的代码,即要完成的功能,能够把这部分称做 Lambda 体。有时候,当咱们想要实现一个函数式接口的那个抽象方法,可是已经有类实现了咱们想要的功能,这个时候咱们就能够用方法引用来直接使用现有类的功能去实现。示例代码以下所示:算法

Person p1 = new Person("Av",18,90);
        Person p2 = new Person("King",20,0);
        Person p3 = new Person("Lance",17,100);
        List<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);

        // 这里咱们须要比较 list 里面的 person,按照年龄排序
        // 那么咱们最多见的作法是
        // sort(List<T> list, Comparator<? super T> c)
        // 一、由于咱们的 sort 方法的第二个参数是一个接口,因此咱们须要实现一个匿名内部类
        Collections.sort(list, new Comparator<Person>() {
            @Override
            public int compare(Person person1, Person person2) {
                return person1.getAge().compareTo(person2.getAge());
            }
        });
        // 二、由于第二个参数是一个 @FunctionalInterface 的函数式接口,因此咱们能够用 lambda 写法
        Collections.sort(list, (person1,person2) -> p1.getScore().compareTo(p2.getAge()));
        // 三、由于第二个参数咱们能够用lambda的方式去实现,
        // 可是恰好又有代码 Comparator.comparing 已经实现了这个功能
        // 这个时候咱们就能够采用方法引用了
        /** * 重点: * 当咱们想要实现一个函数式接口的那个抽象方法,可是已经有类实现了咱们想要的功能, * 这个时候咱们就能够用方法引用来直接使用现有类的功能去实现。 */
        Collections.sort(list, Comparator.comparing(Person::getAge));

        System.out.println(list);
复制代码

其它 Java 内置的函数式接口示例以下所示:编程

public static void main(String[] args) {
    Consumer<String>  c = x->System.out.println(x);
    // 等同于
    Consumer<String> c2 = System.out::print;
}

public static void main(String[] args) {
    BinaryOperator<Double> bo = (n1,n2) ->Math.pow(n1,n2);
    BinaryOperator<Double> bo2 = Math::pow;
}

public static void main(String[] args) {
    BiPredicate<String,String> bp = (str1,str2) ->str1.equals(str2);
    BiPredicate<String,String> bp2 = String::equals;
}
复制代码

注意:当须要引用方法的第一个参数是调用对象,而且第二个参数是须要引用方法的第二个参数(或无参数)时,使用 ClassName::methodNameapi

9)、构造器引用

格式: ClassName :: new 与函数式接口相结合,自动与函数式接口中方法兼容。 能够把构造器引用赋值给定义的方法,可是构造器参数列表要与接口中抽象方法的参数列表一致。示例以下所示:数组

public static void main(String[] args) {
    Supplier<Person> x = ()->new Person();
    Supplier<Person> x2 = Person::new;
}

public static void main(String[] args) {
    Function<String,Person> f  = x->new Person(x);
    Function<String,Person> f2 = Person::new;
}
复制代码

10)、数组引用

格式: type[] :: new,示例以下所示:

public static void main(String[] args) {
   Function<Integer,Person[]> f  = x->new Person[x];
   Function<Integer,Person[]>  f2 = Person[]::new;
}
复制代码

三、Stream API

1)、Stream 是什么?

Stream 是数据渠道,用于操做数据源(集合、数组等)所生成的元素序列。记住:“集合讲的是数据,流讲的是计算!”

2)、特色

  • 1)、Stream 本身不会存储元素
  • 2)、Stream 不会改变源对象。相反,他们会返回一个持有结果的新 Stream
  • 3)、Stream 操做是延迟执行的。这意味着他们会等到须要结果的时候才执行

3)、一个 Stream 操做实例

取出全部大于18岁人的姓名,按字典排序,并输出到控制台,代码以下所示:

private static  List<Person> persons = Arrays.asList(
            new Person("CJK",19,"女"),
            new Person("BODUO",20,"女"),
            new Person("JZ",21,"女"),
            new Person("anglebabby",18,"女"),
            new Person("huangxiaoming",5,"男"),
            new Person("ROY",18,"男")
    );
public static void main(String[] args) throws IOException {
 persons.stream().filter(x-    	>x.getAge()>=18).map(Person::getName).sorted().forEach(System.out::println);
}
复制代码

4)、Stream 的操做三个步骤

  • 一、建立 Stream:一个数据源(如:集合、数组),获取一个流
  • 二、中间操做:一个中间操做链,对数据源的数据进行处理
  • 三、终止操做(终端操做):一个终止操做,执行中间操做链,并产生结果

一、建立 Steam

建立流主要有四种方式,其示例代码以下所示:

@Test
public void test1(){
    //1. Collection 提供了两个方法 stream() 与 parallelStream()
    List<String> list = new ArrayList<>();
    Stream<String> stream = list.stream(); //获取一个顺序流
    Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

    //2. 经过 Arrays 中的 stream() 获取一个数组流
    Integer[] nums = new Integer[10];
    Stream<Integer> stream1 = Arrays.stream(nums);

    //3. 经过 Stream 类中静态方法 of()
    Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);
    
    //4. 建立无限流
    //迭代
    Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
    stream3.forEach(System.out::println);

    //生成
    Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
    stream4.forEach(System.out::println);
}
复制代码

二、中间操做

  • 1)、筛选与切片
    • filter:接收 Lambda ,从流中排除某些元素
    • limit:截断流,使其元素不超过给定数量
    • skip(n):跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
    • distinct:筛选,经过流所生成元素的 hashCode() 和 equals() 去除重复元素
  • 2)、映射
    • map:接收 Lambda ,将元素转换成其余形式或提取信息。接收一个函数做为参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素,相似于 python、go 的 map 语法
    • flatMap:接收一个函数做为参数,将流中的每一个值都换成另外一个流,而后把全部流链接成一个流
  • 3)、排序
    • sorted():天然排序
    • sorted(Comparator com):定制排序

这里,咱们给出一些常见的使用示例,以下所示:

一、有个数组 Integer[] ary = {1,2,3,4,5,6,7,8,9,10},取出中间的第三到第五个元素。
List<Integer> collect = Arrays.stream(ary).skip(2).limit(3).collect(Collectors.toList());
复制代码
二、有个数组 Integer[] ary = {1,2,2,3,4,5,6,6,7,8,8,9,10},取出里面的偶数,并去除重复。
List<Integer> list = Arrays.stream(ary).filter(x -> x % 2 == 0).distinct().collect(Collectors.toList());
Set<Integer> integerSet = Arrays.stream(ary).filter(x -> x % 2 == 0).collect(Collectors.toSet());
复制代码
三、有个二维数组,要求把数组组合成一个一维数组,并排序(1,2,3,4,5……12)

Integer[][] ary = {{3,8,4,7,5}, {9,1,6,2}, {0,10,12,11} };

Arrays.stream(ary).flatMap(item->Arrays.stream(item)).sorted().forEach(System.out::println);
复制代码

3)、终止操做

终端操做会从流的流水线生成结果。其结果能够是任何不是流的值,例如:List、Integer,甚至是 void 。

一、查找与匹配
接口 说明
allMatch(Predicate p) 检查是否匹配全部元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配全部元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 迭代
二、归约

reduce(T iden, BinaryOperator b) 能够将流中元素反复结合起来,获得一个值。返回 Optional。例如使用 reduce 来求全部人员学生的总分的示例代码以下所示:

Integer all = persons.stream().map(Person::getScore).reduce((integer, integer2) -> integer + integer2).get()
复制代码
三、收集
  • collect(Collector c) 将流转换为其余形式。它接收一个 Collector 接口的实现,用于给 Stream 中元素作汇总的方法
  • Collector 接口中方法的实现决定了如何对流执行收集操做(如收集到 List、Set、Map)
  • Collectors 实用类提供了不少静态方法,能够方便地建立常见收集器实例

收集相关的 Stream API 与其实例代码以下所示:

  • 1)、toList List 把流中元素收集到 List:
List<Person> emps= list.stream().collect(Collectors.toList());
复制代码
  • 2)、toSet Set 把流中元素收集 到Set:
Set<Person> emps= list.stream().collect(Collectors.toSet());
复制代码
  • 3)、toCollection Collection 把流中元素收集到建立的集合:
Collection<Person> emps=list.stream().collect(Collectors.toCollection(ArrayList::new));
复制代码
  • 4)、counting Long 计算流中元素的个数:
long count = list.stream().collect(Collectors.counting());
复制代码
  • 5)、summing Int Integer 对流中元素的整数属性求和:
int total=list.stream().collect(Collectors.summingInt(Person::getAge));
复制代码
  • 6)、averaging Int Double 计算流中元素 Integer 属性的平均值:
double avg= list.stream().collect(Collectors.averagingInt(Person::getAge));
复制代码
  • 7)、summarizingInt IntSummaryStatistics 收集流中 Integer 属性的统计值。如平均值:
Int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Person::getAge));
复制代码
  • 8)、joining String 链接流中每一个字符串:
String str= list.stream().map(Person::getName).collect(Collectors.joining());
复制代码
  • 9)、maxBy Optional 根据比较器选择最大值:
Optional<Person> max= list.stream().collect(Collectors.maxBy(comparingInt(Person::getSalary)));
复制代码
  • 10)、minBy Optional 根据比较器选择最小值:
Optional<Person> min = list.stream().collect(Collectors.minBy(comparingInt(Person::getSalary)));
复制代码
  • 11)、reducing 归约产生的类型,从一个做为累加器的初始值开始,利用 BinaryOperator 与流中元素逐个结合,从而归约成单个值:
int total=list.stream().collect(Collectors.reducing(0, Person::getSalary, Integer::sum));
复制代码
  • 12)、collectingAndThen 转换函数返回的类型,包裹另外一个收集器,对其结果转换函数
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
复制代码
  • 13)、groupingBy Map<K, List> 根据某属性值对流分组,属性为 K,结果为 V:
Map<Person.Status, List<Person>> map= list.stream().collect(Collectors.groupingBy(Person::getStatus));
复制代码
  • 14)、partitioningBy Map<Boolean, List> 根 据true 或 false 进行分区:
Map<Boolean,List<Person>>vd= list.stream().collect(Collectors.partitioningBy(Person::getManage));
复制代码
四、终止操做练习案例
  • 1)、取出Person对象的全部名字,放到 List 集合中:
List<String> collect2 = persons.stream().map(Person::getName).collect(Collectors.toList());
复制代码
  • 二、求 Person 对象集合的分数的平均分、总分、最高分,最低分,分数的个数:
IntSummaryStatistics collect = persons.stream().collect(Collectors.summarizingInt(Person::getScore));
System.out.println(collect);
复制代码
  • 三、根据成绩分组,及格的放一组,不及格的放另一组:
Map<Boolean, List<Person>> collect1 = persons.stream().collect(Collectors.partitioningBy(person -> person.getScore() >= 60));
System.out.println(collect1);
复制代码
  • 四、统计 aa.txt 里面的单词数:
public static void main(String[] args) throws IOException {
    InputStream resourceAsStream = Person.class.getClassLoader().getResourceAsStream("aa.txt");
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resourceAsStream));
    bufferedReader.lines().flatMap(x->Stream.of(x.split(" "))).sorted().collect(Collectors.groupingBy(String::toString)).forEach((a,b)-> System.out.println(a+":"+b.size()));
    bufferedReader.close();
}
复制代码

四、复杂泛型

1)、泛型是什么?

泛型,即 “参数化类型”。就是将类型由原来的具体的类型参数化,相似于方法中的变量参数,此时类型也定义成参数形式(能够称之为类型形参),而后在使用/调用时传入具体的类型(类型实参)。

2)、泛型的好处

  • 适用于多种数据类型执行相同的代码
  • 泛型中的类型在使用时指定,不须要强制类型转换

3)、泛型类和泛型接口、泛型方法

泛型的本质是为了参数化类型(在不建立新的类型的状况下,经过泛型指定的不一样类型来控制形参具体限制的类型)。而这种参数类型能够用在类、接口和方法中,分别被称为 泛型类、泛型接口、泛型方法

泛型类

引入一个类型变量T(其余大写字母均可以,不过经常使用的就是T,E,K,V等等),而且用<>括起来,并放在类名的后面。泛型类是容许有多个类型变量的。常见的示例代码以下所示:

public class NormalGeneric<K> {
    private K data;

    public NormalGeneric() {
    }

    public NormalGeneric(K data) {
        this.data = data;
    }

    public K getData() {
        return data;
    }

    public void setData(K data) {
        this.data = data;
    }
}
复制代码
public class NormalGeneric2<T,K> {
    private T data;
    private K result;

    public NormalGeneric2() {
    }

    public NormalGeneric2(T data) {
        this();
        this.data = data;
    }
    
    public NormalGeneric2(T data, K result) {
        this.data = data;
        this.result = result;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public K getResult() {
        return result;
    }

    public void setResult(K result) {
        this.result = result;
    }
}
复制代码

泛型接口

泛型接口与泛型类的定义基本相同。示例代码以下所示:

public interface Genertor<T> {
    public T next();
}
复制代码

可是,实现泛型接口的类,有两种实现方法

一、未传入泛型实参

在 new 出类的实例时,须要指定具体类型:

public class ImplGenertor<T> implements Genertor<T> {
    @Override
    public T next() {
        return null;
    }
}
复制代码
二、传入泛型实参

在 new 出类的实例时,和普通的类没区别。

public class ImplGenertor2 implements Genertor<String> {
    @Override
    public String next() {
        return null;
    }
}
复制代码

泛型方法

泛型方法的 定义在 修饰符与返回值 的中间。示例代码以下所示:

public <T> T genericMethod(T...a){
    return a[a.length/2];
}
复制代码

泛型方法,是在调用方法的时候指明泛型的具体类型,泛型方法能够在任何地方和任何场景中使用,包括普通类和泛型类。

泛型类中定义的普通方法和泛型方法的区别

在普通方法中:

// 虽然在方法中使用了泛型,可是这并非一个泛型方法。
// 这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
// 因此在这个方法中才能够继续使用 T 这个泛型。
public T getKey(){
    return key;
}
复制代码

在泛型方法中:

/** * 这才是一个真正的泛型方法。 * 首先在 public 与返回值之间的 <T> 必不可少,这代表这是一个泛型方法,而且声明了一个泛型 T * 这个 T 能够出如今这个泛型方法的任意位置,泛型的数量也能够为任意多个。 */
public <T,K> K showKeyName(Generic<T> container){
    // ...
}
复制代码

4)、限定类型变量

public class ClassBorder<T extends Comparable> {
...
}

public class GenericRaw<T extends ArrayList&Comparable> {
...
}
复制代码

- <T extends Comparable>T 表示应该绑定类型的子类型,Comparable 表示绑定类型,子类型和绑定类型能够是类也能够是接口

  • extends 左右都容许有多个,如 T,V extends Comparable&Serializable
  • 注意限定类型中,只容许有一个类,并且若是有类,这个类必须是限定列表的第一个
  • 限定类型变量既能够用在泛型方法上也能够用在泛型类上

5)、泛型中的约束和局限性

  • 一、不能用基本类型实例化类型参数。
  • 二、运行时类型查询只适用于原始类型。
  • 三、泛型类的静态上下文中类型变量失效:不能在静态域或方法中引用类型变量。由于泛型是要在对象建立的时候才知道是什么类型的,而对象建立的代码执行前后顺序是 static 的部分,而后才是构造函数等等。因此在对象初始化以前 static 的部分已经执行了,若是你在静态部分引用泛型,那么毫无疑问虚拟机根本不知道是什么东西,由于这个时候类尚未初始化。
  • 四、不能建立参数化类型的数组,可是能够定义参数化类型的数组。
  • 五、不能实例化类型变量。
  • 六、不能使用 try-catch 捕获泛型类的实例。

6)、泛型类型的继承规则

泛型类能够继承或者扩展其余泛型类,好比 List 和 ArrayList:

private static class ExtendPair<T> extends Pair<T>{
...
}
复制代码

7)、通配符类型

  • ?extends X表示类型的上界,类型参数是 X 的子类
  • ?super X表示类型的下界,类型参数是 X 的超类

?extends X

若是其中提供了 get 和 set 类型参数变量的方法的话,set 方法是不容许被调用的,会出现编译错误,而 get 方法则没问题。

?extends X 表示类型的上界,类型参数是 X 的子类,那么能够确定的说,get 方法返回的必定是个 X(无论是 X 或者 X 的子类)编译器是能够肯定知道的。可是 set 方法只知道传入的是个 X,至于具体是 X 的哪一个子类,是不知道的。

所以,?extends X 主要用于安全地访问数据,能够访问 X 及其子类型,而且不能写入非 null 的数据

?super X

若是其中提供了 get 和 set 类型参数变量的方法的话,set 方法能够被调用,且能传入的参数只能是 X 或者 X 的子类。而 get 方法只会返回一个 Object 类型的值。

? super X 表示类型的下界,类型参数是 X 的超类(包括 X 自己),那么能够确定的说,get 方法返回的必定是个 X 的超类,那么究竟是哪一个超类?不知道,可是能够确定的说,Object 必定是它的超类,因此 get 方法返回 Object。编译器是能够肯定知道的。对于 set 方法来讲,编译器不知道它须要的确切类型,可是 X 和 X 的子类能够安全的转型为 X。

所以,?super X 主要用于安全地写入数据,能够写入 X 及其子类型

无限定的通配符 ?

表示对类型没有什么限制,能够把 ?当作全部类型的父类,如 ArrayList<?>

8)、虚拟机是如何实现泛型的?

泛型思想早在 C++ 语言的模板(Template)中就开始生根发芽,在 Java 语言处于尚未出现泛型的版本时,只能经过 Object 是全部类型的父类和类型强制转换两个特色的配合来实现类型泛化

因为 Java 语言里面全部的类型都继承于 java.lang.Object,因此 Object 转型成任何对象都是有可能的。可是也由于有无限的可能性,就只有程序员和运行期的虚拟机才知道这个 Object 究竟是个什么类型的对象。在编译期间,编译器没法检查这个 Object 的强制转型是否成功,若是仅仅依赖程序员去保障这项操做的正确性,许多 ClassCastException 的风险就会转嫁到程序运行期之中。

此外,泛型技术在 C#/C++ 和 Java 之中的使用方式看似相同,但实现上却有着根本性的分歧,C# 里面的泛型不管在程序源码中、编译后的 IL 中(Intermediate Language,中间语言,这时候泛型是一个占位符),或是运行期的 CLR 中,都是切实存在的,List<int> 与 List<String> 就是两个不一样的类型,它们在系统运行期生成,有本身的虚方法表和类型数据,这种实现称为类型膨胀,基于这种方法实现的泛型称为真实泛型

Java 语言中的泛型则不同,它只在程序源码中存在,在编译后的字节码文件中,就已经替换为原来的原生类型(Raw Type,也称为裸类型)了,而且在相应的地方插入了强制转型代码,所以,对于运行期的 Java 语言来讲,ArrayList<int> 与 ArrayList<String> 就是同一个类,因此 泛型技术其实是 Java 语言的一颗语法糖,Java 语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型称为伪泛型。 将一段 Java 代码编译成 Class 文件,而后再用字节码反编译工具进行反编译后,将会发现泛型都不见了,程序又变回了 Java 泛型出现以前的写法,泛型类型都变回了原生类型。

因为 Java 泛型的引入,各类场景(虚拟机解析、反射等)下的方法调用均可能对原有的基础产生影响和新的需求,如在泛型类中如何获取传入的参数化类型等。所以,JCP 组织对虚拟机规范作出了相应的修改,引入了诸如 Signature、LocalVariableTypeTable 等新的属性用于解决伴随泛型而来的参数类型的识别问题,Signature 是其中最重要的一项属性,它的做用就是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并非原生类型,而是包括了参数化类型的信息。修改后的虚拟机规范要求全部能识别 49.0 以上版本的 Class 文件的虚拟机都要能正确地识别 Signature 参数。

最后,从 Signature 属性的出现咱们还能够得出结论,擦除法所谓的擦除,仅仅是对方法的 Code 属性中的字节码进行擦除,实际上元数据中仍是保留了泛型信息,这也是咱们能经过反射手段取得参数化类型的根本依据

2、初识 ByteX

ByteX 使用了纯 Java 来编写源码,它是一个基于 Gradle transform api 和 ASM 的字节码插桩平台。

调试:gradle clean :example:assembleRelease -Dorg.gradle.debug=true --no-daemon

一、优点

  • 1)、自动集成到其它宿主和插件一块儿整合为一个单独的 MainTransformFlow,结合 class 文件多线程并发处理,避免了打包的额外时间呈线性增加
  • 2)、插件、宿主之间彻底解耦,便于协同开发
  • 3)、common module 提供通用的代码复用,每一个插件只需专一自身的字节码插桩逻辑

二、MainTransformFlow 基本流程

在 MainTransformFlow implements MainProcessHandler 常规处理过程,会遍历两次工程构建中的全部 class。

  • 1)、第一次,遍历 traverse 与 traverseAndroidJar 过程,以造成完整的类图。
  • 2)、第二次,执行 transform:再遍历一次工程中全部的构建产物,并对 class 文件作处理后输出。

三、如何自定义独立的 TransformFlow?

重写 IPlugin 的 provideTransformFlow 便可。

四、类图对象

context.getClassGraph() 获取类图对象,两个 TransformFlow 的类图是隔离的。

五、MainProcessHandler

  • 经过复写 process 方法,注册本身的 FlieProcessor 来处理。
  • FileProcessor 采用了责任链模式,每一个 class 文件都会流经一系列的 FileProcessor 来处理。

六、IPlugin.hookTransformName()

使用 反射 Hook 方式 将 Transform 注册到 proguard 以后

3、ByteX 插件平台构建流程探秘

添加 apply plugin: 'bytex' 以后,bytex 能够在 Gradle 的构建流程中起做用了。这里的插件 id 为 bytex,咱们找到 bytex.properties 文件,查看里面映射的实现类,以下所示:

implementation-class=com.ss.android.ugc.bytex.base.ByteXPlugin
复制代码

能够看到,bytex 的实现类为 ByteXPlugin,其源码以下所示:

public class ByteXPlugin implements Plugin<Project> {
    @Override
    public void apply(@NotNull Project project) {
        // 1
        AppExtension android = project.getExtensions().getByType(AppExtension.class);
        // 2
        ByteXExtension extension = project.getExtensions().create("ByteX", ByteXExtension.class);
        // 3
        android.registerTransform(new ByteXTransform(new Context(project, android, extension)));
    }
}
复制代码

首先,注释1处,获取 Android 为 App 提供的扩展属性 AppExtension 实例。而后,在注释2处,获取 ByteX 自身建立的扩展属性 ByteXExtension 实例。最后,在注释3处,注册 ByteXTransform 实例。ByteXTransform 继承了抽象类 CommonTransform,其实现了关键的 transform 方法,其实现源码以下所示:

@Override
public final void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
    super.transform(transformInvocation);
    // 一、若是不是增量模式,则清楚输出目录的文件。
    if (!transformInvocation.isIncremental()) {
        transformInvocation.getOutputProvider().deleteAll();
    }
    // 二、获取 transformContext 实例。
    TransformContext transformContext = getTransformContext(transformInvocation);
    // 三、初始化 HtmlReporter(生成 ByteX 构建产生日志的 HTML 文件)
    init(transformContext);
    // 四、过滤掉没有打开插件开关的 plugin。
    List<IPlugin> plugins = getPlugins().stream().filter(p -> p.enable(transformContext)).collect(Collectors.toList());
    Timer timer = new Timer();
    // 五、建立一个 transformEngine 实例。
    TransformEngine transformEngine = new TransformEngine(transformContext);
    try {
        if (!plugins.isEmpty()) {
            // 六、使用 PriorityQueue 对每个 TransformFlow 进行优先级排序(在这里添加的是与之对应的实现类 MainTransformFlow)。
            Queue<TransformFlow> flowSet = new PriorityQueue<>((o1, o2) -> o2.getPriority() - o1.getPriority());
            MainTransformFlow commonFlow = new MainTransformFlow(transformEngine);
              flowSet.add(commonFlow);
            for (int i = 0; i < plugins.size(); i++) {
                // 七、给每个 Plugin 注册 MainTransformFlow,其实质是将每个 Plugin 的 MainProcessHandler 添加到 MainTransformFlow 中的 handlers 列表中。
                IPlugin plugin = plugins.get(i);
                TransformFlow flow = plugin.registerTransformFlow(commonFlow, transformContext);
                if (!flowSet.contains(flow)) {
                    flowSet.add(flow);
                }
            }
            while (!flowSet.isEmpty()) {
                TransformFlow flow = flowSet.poll();
                if (flow != null) {
                    if (flowSet.size() == 0) {
                        flow.asTail();
                    }
                    // 八、按指定优先级执行每个 TransformFlow 的 run 方法,默认只有一个 MainTransformFlow 实例。
                    flow.run();
                    // 九、获取流中的 graph 类图对象并清除。
                    Graph graph = flow.getClassGraph();
                    if (graph != null) {
                        //clear the class diagram.we won’t use it anymore
                        graph.clear();
                    }
                }
            }
        } else {
            transformEngine.skip();
        }
        // 10
        afterTransform(transformInvocation);
    } catch (Throwable throwable) {
        LevelLog.sDefaultLogger.e(throwable.getClass().getName(), throwable);
        throw throwable;
    } finally {
        for (IPlugin plugin : plugins) {
            try {
                plugin.afterExecute();
            } catch (Throwable throwable) {
                LevelLog.sDefaultLogger.e("do afterExecute", throwable);
            }
        }
        transformContext.release();
        release();
        timer.record("Total cost time = [%s ms]");
        if (BooleanProperty.ENABLE_HTML_LOG.value()) {
            HtmlReporter.getInstance().createHtmlReporter(getName());
            HtmlReporter.getInstance().reset();
        }
    }
}
复制代码

在注释7处,调用了 plugin.registerTransformFlow 方法,其源码以下所示:

@Nonnull
@Override
public final TransformFlow registerTransformFlow(@Nonnull MainTransformFlow mainFlow, @Nonnull TransformContext transformContext) {
    if (transformFlow == null) {
        transformFlow = provideTransformFlow(mainFlow, transformContext);
        if (transformFlow == null) {
            throw new RuntimeException("TransformFlow can not be null.");
        }
    }
    return transformFlow;
}
复制代码

这里继续调用了 provideTransformFlow 方法,其源码以下所示:

/** * create a new transformFlow or just return mainFlow and append a handler. * It will be called by {@link IPlugin#registerTransformFlow(MainTransformFlow, TransformContext)} when * handle start. * * @param mainFlow main TransformFlow * @param transformContext handle context * @return return a new TransformFlow object if you want make a new flow for current plugin */
protected TransformFlow provideTransformFlow(@Nonnull MainTransformFlow mainFlow, @Nonnull TransformContext transformContext) {
    return mainFlow.appendHandler(this);
}
复制代码

能够看到,经过调用 mainFlow.appendHandler(this) 方法将每个 Plugin 的 MainProcessHandler 添加到 MainTransformFlow 中的 handlers 列表之中。

在注释8处,按指定优先级执行了每个 TransformFlow 的 run 方法,默认只有一个 MainTransformFlow 实例。咱们看到了 MianTransformFlow 的 run 方法:

@Override
public void run() throws IOException, InterruptedException {
    try {
        // 1
        beginRun();
        // 2
        runTransform();
    } finally {
        // 3
        endRun();
    }
}
复制代码

首先,在注释1出,调用了 beginRun 方法,其实现以下:

// AbsTransformFlow
protected void beginRun() {
    transformEngine.beginRun();
}

// TransformEngine
public void beginRun(){
    context.markRunningState(false);
}

// TransformContext
private final AtomicBoolean running = new AtomicBoolean(false);

void markRunningState(boolean running) {
    this.running.set(running);
}
复制代码

最后,在 TransformContext 实例中使用了一个 AtomicBoolean 实例标记 MainTransformFlow 是否正在运行中。

而后,在注释2处执行了 runTransform 方法,这里就是真正执行 transform 的地方,其源码以下所示:

private void runTransform() throws IOException, InterruptedException {
    if (handlers.isEmpty()) return;
    Timer timer = new Timer();
    timer.startRecord("PRE_PROCESS");
    timer.startRecord("INIT");
    // 一、初始化 handlers 列表中的每个 handler。
    for (MainProcessHandler handler : handlers) {
        handler.init(transformEngine);
    }
    timer.stopRecord("INIT", "Process init cost time = [%s ms]");
    // 若是不是 跳过 traverse 仅仅只执行 Transform 方法时,才执行 traverse 过程。
    if (!isOnePassEnough()) {
        if (!handlers.isEmpty() && context.isIncremental()) {
            timer.startRecord("TRAVERSE_INCREMENTAL");
            // 二、若是是 增量模式,则执行 traverseArtifactOnly(仅仅增量遍历产物)调用每个 plugin 的对应的 MainProcessHandler 的 traverseIncremental 方法。这里最终会调用 ClassFileAnalyzer.handle 方法进行遍历分发操做。
            traverseArtifactOnly(getProcessors(Process.TRAVERSE_INCREMENTAL, new ClassFileAnalyzer(context, Process.TRAVERSE_INCREMENTAL, null, handlers)));
            timer.stopRecord("TRAVERSE_INCREMENTAL", "Process project all .class files cost time = [%s ms]");
        }
        handlers.forEach(plugin -> plugin.beforeTraverse(transformEngine));
        timer.startRecord("LOADCACHE");
        // 三、建立一个 CachedGraphBuilder 对象:可以缓存 类图 的 类图构建者对象。
        GraphBuilder graphBuilder = new CachedGraphBuilder(context.getGraphCache(), context.isIncremental(), context.shouldSaveCache());
        if (context.isIncremental() && !graphBuilder.isCacheValid()) {
            // 四、若是是增量更新 && graphBuilder 的缓存失效则直接请求非增量运行。
            context.requestNotIncremental();
        }
        timer.stopRecord("LOADCACHE", "Process loading cache cost time = [%s ms]");
        // 五、内部会调用 running.set(true) 来标记正在运行的状态。
        running();
        if (!handlers.isEmpty()) {
            timer.startRecord("PROJECT_CLASS");
            // 六、执行 traverseArtifactOnly(遍历产物)调用每个 plugin 的对应的 MainProcessHandler 的 traverse 方法,这里最终会调用 ClassFileAnalyzer.handle 方法进行遍历分发操做。
            traverseArtifactOnly(getProcessors(Process.TRAVERSE, new ClassFileAnalyzer(context, Process.TRAVERSE, graphBuilder, handlers)));
            timer.stopRecord("PROJECT_CLASS", "Process project all .class files cost time = [%s ms]");
        }
        if (!handlers.isEmpty()) {
            timer.startRecord("ANDROID");
            // 七、仅仅遍历 Android.jar
            traverseAndroidJarOnly(getProcessors(Process.TRAVERSE_ANDROID, new ClassFileAnalyzer(context, Process.TRAVERSE_ANDROID, graphBuilder, handlers)));
            timer.stopRecord("ANDROID", "Process android jar cost time = [%s ms]");
        }
        timer.startRecord("SAVECACHE");
        // 八、构建 mClassGraph 类图实例。
        mClassGraph = graphBuilder.build();
        timer.stopRecord("SAVECACHE", "Process saving cache cost time = [%s ms]");
    }
    timer.stopRecord("PRE_PROCESS", "Collect info cost time = [%s ms]");
    if (!handlers.isEmpty()) {
        timer.startRecord("PROCESS");
        // 九、遍历执行每个 plugin 的 transform 方法。
        transform(getProcessors(Process.TRANSFORM, new ClassFileTransformer(handlers, needPreVerify(), needVerify())));
        timer.stopRecord("PROCESS", "Transform cost time = [%s ms]");
    }
}
复制代码

首先,在注释1处,遍历调用了每个 MainProcessHandler 的 init 方法,它是用于 transform 开始前的初始化实现方法。

MainProcessHandler 接口的 init 方法是一个 default 方法,里面直接调用了每个 pluign 实现的 init 方法(若是 plugin 没有实现,则仅仅调用 CommonPlugin 实现的 init 方法:这里一般是用于把不须要处理的文件添加到 mWhiteList 列表),这里能够作一些 plugin 的准备工做。

一、仅仅遍历产物

traverseArtifactOnly(getProcessors(Process.TRAVERSE, new ClassFileAnalyzer(context, Process.TRAVERSE, graphBuilder, handlers)));
复制代码

getProcessors 方法的源码以下所示:

private FileProcessor[] getProcessors(Process process, FileHandler fileHandler) {
    List<FileProcessor> processors = handlers.stream()
            .flatMap((Function<MainProcessHandler, Stream<FileProcessor>>) handler -> handler.process(process).stream())
            .collect(Collectors.toList());
    switch (process) {
        case TRAVERSE_INCREMENTAL:
            processors.add(0, new FilterFileProcessor(fileData -> fileData.getStatus() != Status.NOTCHANGED));
            processors.add(new IncrementalFileProcessor(handlers, ClassFileProcessor.newInstance(fileHandler)));
            break;
        case TRAVERSE:
        case TRAVERSE_ANDROID:
        case TRANSFORM:
            processors.add(ClassFileProcessor.newInstance(fileHandler));
            processors.add(0, new FilterFileProcessor(fileData -> fileData.getStatus() != Status.NOTCHANGED && fileData.getStatus() != Status.REMOVED))
            break;
        default:
            throw new RuntimeException("Unknow Process:" + process);
    }
    return processors.toArray(new FileProcessor[0]);
}
复制代码

这里的 processor 的添加由 增量 进行界定,具体的处理标准以下:

  • TRAVERSE_INCREMENTAL
    • FilterFileProcessor按照不一样的过程过滤掉不须要的 FileData
    • IncrementalFileProcessor用于进行 增量文件的处理
  • TRAVERSE/TRAVERSE_ANDROID/TRANSFORM:
    • FilterFileProcessor按照不一样的过程过滤掉不须要的 FileData
    • ClassFileProcessor用于处理 .class 文件

二、仅仅遍历 Android Jar 包

traverseAndroidJarOnly(getProcessors(Process.TRAVERSE_ANDROID, new ClassFileAnalyzer(context, Process.TRAVERSE_ANDROID, graphBuilder, handlers)));
复制代码

三、构建 mClassGraph 类图对象

mClassGraph = graphBuilder.build();
复制代码

四、执行 Transform

transform(getProcessors(Process.TRANSFORM, new ClassFileTransformer(handlers, needPreVerify(), needVerify())));
复制代码

transform 的源码以下所示:

// AbsTransformFlow 类中
protected AbsTransformFlow transform(FileProcessor... processors) throws IOException, InterruptedException {
        beforeTransform(transformEngine);
        transformEngine.transform(isLast, processors);
        afterTransform(transformEngine);
        return this;
    }
复制代码

1)、beforeTransform(transformEngine)

// MainTransformFlow
@Override
protected AbsTransformFlow beforeTransform(TransformEngine transformEngine) {
    // 1
    handlers.forEach(plugin -> plugin.beforeTransform(transformEngine));
    return this;
}
复制代码

注释1处,遍历执行 每个 plugin 的 beforeTransform 方法作一些自身 transform 前的准备工做。

2)、transformEngine.transform(isLast, processors)

// TranformEngine
public void transform(boolean isLast, FileProcessor... processors) {
    Schedulers.FORKJOINPOOL().invoke(new PerformTransformTask(context.allFiles(), getProcessorList(processors), isLast, context));
}
复制代码

Shedulers.FORKJOINPOOL() 方法的源码以下所示:

public class Schedulers {
    private static final int cpuCount = Runtime.getRuntime().availableProcessors();
    private final static ExecutorService IO = new ThreadPoolExecutor(0, cpuCount * 3,
            30L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>());

    // 1
    private static final ExecutorService COMPUTATION = Executors.newWorkStealingPool(cpuCount);

    public static Worker IO() {
        return new Worker(IO);
    }

    public static Worker COMPUTATION() {
        return new Worker(COMPUTATION);
    }

    public static ForkJoinPool FORKJOINPOOL() {
        return (ForkJoinPool) COMPUTATION;
    }
}
复制代码

能够看到,最终是执行 Executors.newWorkStealingPool(cpuCount) 方法生成了一个 ForkJoinPool 实例。

ForkJoinPool 与 ThreadPoolExecutor 是属于平级关系,ForkJoinPool 线程池是为了实现“分治法”这一思想而建立的,经过把大任务拆分红小任务,而后再把小任务的结果汇总起来就是最终的结果,和 MapReduce 的思想很相似。除了“分治法”以外,ForkJoinPool 还使用了工做窃取算法,即全部线程均尝试找到并执行已提交的任务,或是经过其余任务建立的子任务。有了它咱们就能够尽可能避免一个线程执行完本身的任务后“无所事事”的状况。

而后这里会回调 PerformTransformTask 实例的 compute 方法,源码以下所示:

@Override
protected void compute() {
    if (outputFile) {
        // 一、若是是最后一个 TransformFlow,则递归调用全部的 FileTransformTask。
        List<FileTransformTask> tasks = source.map(cache -> new FileTransformTask(context, cache, processors)).collect(Collectors.toList());
        // 二、对于Fork/Join模式,假如Pool里面线程数量是固定的,那么调用子任务的fork方法至关于A先分工给B,而后A当监工不干活,B去完成A交代的任务。因此上面的模式至关于浪费了一个线程。那么若是使用invokeAll至关于A分工给B后,A和B都去完成工做。这样能够更好的利用线程池,缩短执行的时间。
        invokeAll(tasks);
    } else {
        // 三、、递归调用 FileTransformTask
        PerformTraverseTask traverseTask = new PerformTraverseTask(source, processors);
        invokeAll(traverseTask);
    }
}
复制代码

在注释1处,若是是最后一个 TransformFlow,则调用全部的 FileTransformTask。注释2处,对于 Fork/Join 模式,假如 Pool 里面线程数量是固定的,那么调用子任务的 fork 方法至关于 A 先分工给 B,而后 A 当监工不干活,B 去完成 A 交代的任务。因此上面的模式至关于浪费了一个线程。那么若是使用 invokeAll 至关于 A 分工给 B 后,A 和 B 都去完成工做。这样能够更好的利用线程池,缩短执行的时间。注释3处,执行了 ForkJoinTask 的 invokeAll 方法,这里便会回调 compute 方法,源码以下所示:

@Override
protected void compute() {
    List<FileTraverseTask> tasks = source.map(cache -> new FileTraverseTask(cache, processors)).collect(Collectors.toList());
    // 1
    invokeAll(tasks);
}
复制代码

注释1处,继续回调全部的 FileTraverseTask 实例的 compute 方法,源码以下所示:

@Override
protected void compute() {
    List<TraverseTask> tasks = fileCache.stream().map(file -> new TraverseTask(fileCache, file, processors))
            .toList().blockingGet();
    // 1
    invokeAll(tasks);
}
复制代码

注释1处,继续回调全部的 TraverseTask 实例的 compute 方法,源码以下所示:

@Override
protected void compute() {
    try {
        Input input = new Input(fileCache.getContent(), file);
        ProcessorChain chain = new ProcessorChain(processors, input, 0);
        // 一、调用 ProcessorChain 的 proceed 方法。
        chain.proceed(input);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
复制代码

注释1处,调用了 ProcessorChain 的 proceed 方法。源码以下所示:

@Override
public Output proceed(Input input) throws IOException {
    if (index >= processors.size()) throw new AssertionError();
    // 1
    FileProcessor next = processors.get(index);
    
    return next.process(new ProcessorChain(processors, input, index + 1));
}
复制代码

注释1处,会从 processors 处理器列表中获取第一个处理器—FilterFileProcessor,并调用它的 process 方法,源码以下所示:

@Override
public Output process(Chain chain) throws IOException {
    Input input = chain.input();
    if (predicate.test(input.getFileData())) {
        // 1
        return chain.proceed(input);
    } else {
        return new Output(input.getFileData());
    }
}
复制代码

注释1处,若是有 FileData 的话,则继续调用 chain 的 proceed 方法,内部会继续调用 ClassFileProcessor 的 process 方法,源码以下:

@Override
public Output process(Chain chain) throws IOException {
    Input input = chain.input();
    FileData fileData = input.getFileData();
    if (fileData.getRelativePath().endsWith(".class")) {
        // 一、若是 fileData 是 .class 文件,则调用 ClassFileTransformer 的 handle 方法进行处理。
        handler.handle(fileData);
    }
    // 二、
    return chain.proceed(input);
}
复制代码

注释1处,若是 fileData 是 .class 文件,则调用 ClassFileTransformer 的 handle 方法进行处理。其源码以下所示:

@Override
public void handle(FileData fileData) {
    try {
        byte[] raw = fileData.getBytes();
        String relativePath = fileData.getRelativePath();
        int cwFlags = 0;  //compute nothing
        int crFlags = 0;
        for (MainProcessHandler handler : handlers) {
            // 一、设置 ClassWrite 的 flag 的默认值为 ClassWriter.COMPUTE_MAXS。
            cwFlags |= handler.flagForClassWriter();
            if ((handler.flagForClassReader(Process.TRANSFORM) & ClassReader.EXPAND_FRAMES) == ClassReader.EXPAND_FRAMES) {
                crFlags |= ClassReader.EXPAND_FRAMES;
            }
        }
        ClassReader cr = new ClassReader(raw);
        ClassWriter cw = new ClassWriter(cwFlags);
        ClassVisitorChain chain = getClassVisitorChain(relativePath);
        if (needPreVerify) {
            // 二、若是须要预校验,则将责任链表头尾部设置为 AsmVerifyClassVisitor 实例。
            chain.connect(new AsmVerifyClassVisitor());
        }
        if (handlers != null && !handlers.isEmpty()) {
            for (MainProcessHandler handler : handlers) {
                // 三、遍历执行全部 plugin 的 transform。其内部会使用 chain.connect(new ReferCheckClassVisitor(context)) 的方式 
                if (!handler.transform(relativePath, chain)) {
                    fileData.delete();
                    return;
                }
            }
        }
        // 四、兼容 ClassNode 处理的模式
        ClassNode cn = new SafeClassNode();
        chain.append(cn);
        chain.accept(cr, crFlags);
        for (MainProcessHandler handler : handlers) {
            if (!handler.transform(relativePath, cn)) {
                fileData.delete();
                return;
            }
        }
        cn.accept(cw);
        if (!GlobalWhiteListManager.INSTANCE.shouldIgnore(fileData.getRelativePath())) {
            raw = cw.toByteArray();
            if (needVerify) {
                ClassNode verifyNode = new ClassNode();
                new ClassReader(raw).accept(verifyNode, crFlags);
                AsmVerifier.verify(verifyNode);
            }
            // 五、若是不是白名单里的文件,则将 ClassWriter 中的数据放入 fileData 之中。
            fileData.setBytes(raw);
        }
    } catch (ByteXException e) {
        throw e;
    } catch (Exception e) {
        LevelLog.sDefaultLogger.e(String.format("Failed to handle class %s", fileData.getRelativePath()), e);
        if (!GlobalWhiteListManager.INSTANCE.shouldIgnore(fileData.getRelativePath())) {
            throw e;
        }
    }
}
复制代码

在 ClassFileProcessor 的 process 方法的注释2处,又会继续调用 ProcessorChain 的 proceed 方法,这里会回调 BackupFileProcessor 实例的 process 方法,源码以下所示:

@Override
public Output process(Chain chain) throws IOException {
    Input input = chain.input();
    // 仅仅是返回处理过的输出文件
    return new Output(input.getFileData());
}
复制代码

按照这样的模式,PerformTraverseTask 实例的全部 task 都被遍历执行了。

最后,便会调用 MainTransformFlow 实例的 afterTransform 方法,源码以下:

@Override
protected AbsTransformFlow afterTransform(TransformEngine transformEngine) {
    handlers.forEach(plugin -> plugin.afterTransform(transformEngine));
    return this;
}
复制代码

这里遍历执行了全部 plugin 的 afterTransform 方法。

而后,咱们再回到 CommonTransform 的 transform 方法,在执行完 MainTransformFlow 的 run 方法后,便会调用注释9处的代码来获取流中的 graph 类图对象并清除。最后,执行注释10处的 afterTransform 方法用来作 transform 以后的收尾工做。

4、总结

在本文中,咱们一块儿对 ByteX 插件平台的构建流程进行了探秘。从 ByteX 的源码实现中,咱们能够看出做者对 函数式编程、Java 1.8 Lambda 表达式、Java 1.8 Stream API、复杂泛型 等技术的灵活运用,因此,几乎全部看似很 🐂 的轮子,其实质都是依赖于对基础技术的深度掌握。那么,如何才能达到深度掌握基础技术的程度呢?— 惟有不断地练习与有规律的复习

参考连接:


很感谢您阅读这篇文章,但愿您能将它分享给您的朋友或技术群,这对我意义重大。

但愿咱们能成为朋友,在 Github掘金上一块儿分享知识。

相关文章
相关标签/搜索