APT技术

一、什么是APT
APT(Annotation Processing Tool) 注解处理器,是 javac 的一个工具,他可以在源码生成class的时候,处理Java语法树。
我们用他可以干什么呢?
- lombok的原理,在编译期修改字节码,生成
get和set方法。
本文我们就使用APT技术, 来自动生成get和set方法

感兴趣的可以了解一下,不用害怕,尤其是2.6到2.7目录,不需要看到这个API就害怕。也不用死记硬背。了解就好。 正经人是不会用这个生成字节码的,字节码生成我们有很多好用的工具。
- [Javassist]
或者是 javapoet
二、实战演示
2.1 定义处理器
继承 AbstractProcessor
1@AutoService(Processor.class)
2@SupportedAnnotationTypes({"cn.lxchinesszz.MyData","cn.lxchinesszz.MyGetter","cn.lxchinesszz.MySetter"})
3// 这个注解处理器是处理哪个注解的
4@SupportedSourceVersion(SourceVersion.RELEASE_8)
5public class MyLombokProcessor extends AbstractProcessor {
6
7 @Override
8 public synchronized void init(ProcessingEnvironment processingEnvironment) {}
9
10 @Override
11 public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {}
12}- @AutoService 谷歌提供的SPI工具。当使用这个注解会自定生成Java SPI文件, 当然如果不想用谷歌的工具,我们也可以自己来写配置文件
1├── classes
2│ ├── META-INF
3│ │ └── services
4│ │ └── javax.annotation.processing.Processor- @SupportedAnnotationTypes({"cn.lxchinesszz.MyData","cn.lxchinesszz.MyGetter","cn.lxchinesszz.MySetter"})
支持的注解类型
- @SupportedSourceVersion(SourceVersion.RELEASE_8)
支持的源码类型
2.2 Element 体系
roundEnv.getElementsAnnotatedWith(MyData.class)可以获取被该注解修饰的类或者字段或者方法。
下面我们看下 Element 的类型。

1public class User{ // TypeElement
2
3 private String name; // VariableElement
4
5 private Interge age; // VariableElement
6
7 public String getName(){ // ExecutableElement
8 return this.name;
9 }
10
11 public void setName( // ExecutableElement
12 String name // VariableElement
13 ){
14 this.name = name;
15 }
16}如何知道Element 的类型呢。
Element#getKind

2.2.1 获取字段信息
这里我们先自定义一个字段类型来获取基础信息,来学习 Element
1 public class FieldElement extends ModifierElement {
2
3 /**
4 * 字段名
5 */
6 private final String fieldName;
7
8 /**
9 * 字段类型
10 */
11 private Class<?> fieldType;
12
13 /**
14 * 资源原始类型
15 */
16 private final VariableElement fieldElement;
17
18 /**
19 * 基本类型提示
20 */
21 private String remark;
22
23 /**
24 * 字段所属类
25 */
26 private final TypeElement classElement;
27
28 public FieldElement(String fieldName, VariableElement fieldElement) {
29 super(fieldElement);
30 this.fieldName = fieldName;
31 this.fieldElement = fieldElement;
32 this.classElement = (TypeElement) fieldElement.getEnclosingElement();
33 try {
34 if (isPrimitive()) {
35 fieldType = null;
36 this.remark = "基本类型:" + fieldElement.asType().toString();
37 } else {
38 this.fieldType = Class.forName(fieldElement.asType().toString());
39 }
40 } catch (ClassNotFoundException e) {
41 // 如果还报错说明是一个泛型 根据泛型类型来进行处理 fieldElement.asType()
42 // DeclaredType Set<String>
43 // WildcardType
44 // ?
45 // ? extends Number
46 // ? super T
47 this.fieldType = Object.class;
48 }
49 }
50 }- 首先先判断是字段类型
1 public static FieldElement toFiledElement(Element enclosedElement) {
2 if (ElementKind.FIELD.equals(enclosedElement.getKind())) {
3 VariableElement fieldElement = (VariableElement) enclosedElement;
4 Name simpleName = fieldElement.getSimpleName();
5 return new FieldElement(simpleName.toString(), fieldElement);
6 } else {
7 throw new RuntimeException("enclosedElement 不是字段类型:" + enclosedElement);
8 }
9 }
102.2.1 获取方法信息
- 方法包括方法参数和返回值,这里我们自定义一个方法参数。
1public class MethodElement extends ModifierElement{
2
3 /**
4 * 方法参数名
5 */
6 private final String methodName;
7
8 /**
9 * 返回值
10 */
11 private Class<?> returnType;
12
13 /**
14 * 方法原始信息
15 */
16 private final ExecutableElement methodElement;
17
18 /**
19 * 方法参数
20 */
21 private final List<MethodParamElement> methodParamElements;
22
23 public MethodElement(ExecutableElement methodElement, List<MethodParamElement> methodParamElements) {
24 super(methodElement);
25 this.methodName = methodElement.getSimpleName().toString();
26 try {
27 TypeMirror returnTypeMirror = methodElement.getReturnType();
28 if (returnTypeMirror instanceof NoType) {
29 this.returnType = Void.TYPE;
30 } else {
31 this.returnType = Class.forName(methodElement.getReturnType().toString());
32 }
33 } catch (ClassNotFoundException e) {
34 this.returnType = Void.TYPE;
35 }
36 this.methodElement = methodElement;
37 this.methodParamElements = methodParamElements;
38 }
39}- 生成方法
1 public static MethodParamElement toMethodParamElement(Element enclosedElement) {
2 if (ElementKind.PARAMETER.equals(enclosedElement.getKind())) {
3 VariableElement fieldElement = (VariableElement) enclosedElement;
4 Name simpleName = fieldElement.getSimpleName();
5 return new MethodParamElement(simpleName.toString(), fieldElement);
6 } else {
7 throw new RuntimeException("enclosedElement 不是字段类型:" + enclosedElement);
8 }
9 }
10
11 public static MethodElement toMethodElement(Element enclosedElement) {
12 if (ElementKind.METHOD.equals(enclosedElement.getKind())) {
13 ExecutableElement methodElement = (ExecutableElement) enclosedElement;
14 List<? extends VariableElement> parameters = methodElement.getParameters();
15 List<MethodParamElement> paramElements = new ArrayList<>();
16 for (VariableElement parameter : parameters) {
17 paramElements.add(toMethodParamElement(parameter));
18 }
19 return new MethodElement(methodElement, paramElements);
20 } else {
21 throw new RuntimeException("enclosedElement 不是方法类型:" + enclosedElement.getClass());
22 }
23 }2.2.2 获取类信息
类信息包括字段和方法
1public class ClassElement extends ModifierElement {
2
3 /**
4 * 类名称
5 */
6 private final String className;
7
8 /**
9 * 包名称
10 */
11 private final String packageName;
12
13 /**
14 * 类原始信息
15 */
16 private final TypeElement classElement;
17
18 /**
19 * 字段信息
20 */
21 private final List<FieldElement> fieldElements;
22
23 /**
24 * 方法信息
25 */
26 private final List<MethodElement> methodElements;
27
28 public ClassElement(Element enclosedElement, List<FieldElement> fieldElements, List<MethodElement> methodElements) {
29 super(enclosedElement);
30 this.classElement = (TypeElement) enclosedElement;
31 this.fieldElements = fieldElements;
32 this.methodElements = methodElements;
33 this.className = classElement.getSimpleName().toString();
34 this.packageName = classElement.getQualifiedName().toString().replaceAll("." + classElement.getSimpleName().toString(), "");
35 }
36}生成类信息
1public static ClassElement toClassElement(Element enclosedElement) {
2 if (ElementKind.CLASS.equals(enclosedElement.getKind())) {
3 List<? extends Element> enclosedElements = enclosedElement.getEnclosedElements();
4 List<FieldElement> fieldElements = new ArrayList<>();
5 List<MethodElement> methodElements = new ArrayList<>();
6 for (Element element : enclosedElements) {
7 if (ElementKind.FIELD.equals(element.getKind())) {
8 fieldElements.add(toFiledElement(element));
9 }
10 if (ElementKind.METHOD.equals(element.getKind())) {
11 methodElements.add(toMethodElement(element));
12 }
13 }
14 return new ClassElement(enclosedElement, fieldElements, methodElements);
15 } else {
16 throw new RuntimeException("enclosedElement 不是字段类型:" + enclosedElement);
17 }
18}
192.3 日志打印
APT方法中日志的打印,要使用工具。在初始化方法中获取消息打印实例。
1public class MyLombokProcessor extends AbstractProcessor {
2 private Messager message;
3
4 @Override
5 public synchronized void init(ProcessingEnvironment processingEnvironment) {
6 super.init(processingEnvironment);
7 message = processingEnvironment.getMessager();
8 }
9
10 @Override
11 public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
12 // 扫描所有被@MyData注解的元素
13 processingEnv.getMessager().printMessage(NOTE, "------------MyData-----------" + roundEnv.getElementsAnnotatedWith(MyData.class));
14 }
15}就想log日志一样,他也是有消息类型的,如: 提示、异常、警告等。如下枚举
1/**
2 * 诊断类型,例如错误或警告。诊断的类型可用于确定应如何将诊断呈现给用户。例如,错误可能被涂成红色或以“错误”一词为前缀,
3 * 而警告可能被涂成黄色或以“警告”一词为前缀。没有要求 Kind 应该对诊断消息暗示任何固有的语义含义:例如,一个工具可能会
4 * 提供一个选项来将所有警告报告为错误。
5 */
6enum Kind {
7 /**
8 * 阻止工具正常完成编译
9 */
10 ERROR,
11 /**
12 * 警告
13 */
14 WARNING,
15 /**
16 * 类似于警告的问题,但由工具规范强制要求。例如,Java™ 语言规范要求对某些未经检查的操作和使用过时的方法发出警告。
17 */
18 MANDATORY_WARNING,
19 /**
20 * 来自该工具的信息性消息。
21 */
22 NOTE,
23 /**
24 * 其他类型的诊断
25 */
26 OTHER,
27 }2.4 字节码修改
字节码修改首先我们要拿到字节码语法树对象,通过观察者模式类进行修改。这里也在初始化时候获取工具。 如下我们先定义一个工具。
1public class ClassElementBuilder {
2
3 private ProcessingEnvironment processingEnv;
4
5 private JavacTrees trees;
6
7 protected Names names;
8
9 protected TreeMaker treeMaker;
10
11
12 public ClassElementBuilder(ProcessingEnvironment processingEnv) {
13 Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
14 this.processingEnv = processingEnv;
15 this.trees = JavacTrees.instance(processingEnv);
16 this.treeMaker = TreeMaker.instance(context);
17 this.names = Names.instance(context);
18 }
19}处理器初始化方法进行工具的实例化。
1 @Override
2 public synchronized void init(ProcessingEnvironment processingEnvironment) {
3 super.init(processingEnvironment);
4 this.classElementBuilder = new ClassElementBuilder(processingEnvironment);
5 }此时我们就能对添加和修改语法树了。但是这里我们先不着急, 我们在先学习一下语法树的API。
2.5 JCTree 语法树
2.5.1 定义字段
定义变量使用
- TreeMaker#VarDef(JCTree.JCModifiers 字段修饰符,Names 字段名,JCExpression 字段类型,JCExpression 赋值语句)
1private String ${fieldName};
2private JCTree.JCVariableDecl generateStringField(JCTree.JCClassDecl jcClassDecl, String fieldName) {
3 JCTree.JCVariableDecl var = treeMaker.VarDef(
4 treeMaker.Modifiers(Flags.PRIVATE),
5 names.fromString(fieldName),
6 treeMaker.Ident(names.fromString("String")),
7 null);
8 jcClassDecl.defs = jcClassDecl.defs.prepend(var);
9 return var;
10}
11
12private String ${fieldName} = ${fieldName}
13private JCTree.JCVariableDecl generateStringField(JCTree.JCClassDecl jcClassDecl, String fieldName) {
14 // 字段的赋值语句
15 JCTree.JCVariableDecl var = treeMaker.VarDef(
16 treeMaker.Modifiers(Flags.PRIVATE),
17 names.fromString(fieldName),
18 treeMaker.Ident(names.fromString("String")),
19 treeMaker.Literal(fieldName));
20
21 jcClassDecl.defs = jcClassDecl.defs.prepend(var);
22 return var;
23}要想理解这个API,实现要分析字段是由什么构成的,正如下图。

标示符三种处理方式。
- 包装类型,不用引入包,可以直接使用
- TreeMaker#Ident JCExpression
1treeMaker.Ident(names.fromString("String"))- 基本类型,不用引入包,可以直接使用
- TreeMaker#TypeIdent
1treeMaker.TypeIdent(TypeTag.INT)- 引用类型,需要引入包后再直接使用
- 先引入包,然后就向包装类型那样进行处理。
1// import package
2private JCTree.JCImport genImportPkg(String packageName, String className) {
3 JCTree.JCIdent ident = treeMaker.Ident(names.fromString(packageName));
4 return treeMaker.Import(treeMaker.Select(
5 ident, names.fromString(className)), false);
6}2.5.2 定义get和set方法
生成set方法,方法是由
- 方法修饰符
treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC + Flags.FINAL) - 方法名
names.fromString("setName") - 方法返回值
treeMaker#Type、treeMaker#TypeIdent
1/**
2 * public void setName(String name){
3 * this.name = name;
4 * }
5 *
6 * @param jcClassDecl 类
7 * @param f 字段
8 * @param fieldName 字段名
9 */
10private void generateSetMethod(JCTree.JCClassDecl jcClassDecl, JCTree.JCVariableDecl f, String fieldName) {
11 // 方法体内容
12 ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
13 // this.MyDate
14 JCTree.JCFieldAccess aThis = treeMaker.Select(treeMaker.Ident(names.fromString("this")), names.fromString(fieldName));
15 // this.MyDate = MyDate;
16 JCTree.JCExpressionStatement exec = treeMaker.Exec(treeMaker.Assign(aThis, treeMaker.Ident(names.fromString(fieldName))));
17 statements.add(exec);
18 JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
19
20 // 方法参数
21 JCTree.JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), names.fromString(fieldName), f.vartype, null);
22 com.sun.tools.javac.util.List<JCTree.JCVariableDecl> parameters = com.sun.tools.javac.util.List.of(param);
23
24 JCTree.JCMethodDecl getNameMethod = treeMaker.MethodDef(
25 treeMaker.Modifiers(Flags.PUBLIC), // 方法修饰符
26 names.fromString("set" + capRename(fieldName)), // 方法名,capName转驼峰
27 treeMaker.Type(new Type.JCVoidType()), // 方法返回值类型
28 List.nil(),
29 parameters, // 方法参数
30 List.nil(),
31 body,// 方法体
32 null
33 );
34 // 插入到语法树中
35 jcClassDecl.defs = jcClassDecl.defs.prepend(getNameMethod);
36}
37
38/**
39 * public void getName(){
40 * return this.name;
41 * }
42 *
43 * @param jcClassDecl 类
44 * @param f 字段
45 * @param fieldName 字段名
46 */
47private void generateGetMethod(JCTree.JCClassDecl jcClassDecl, JCTree.JCVariableDecl f, String fieldName) {
48 // 方法体内容
49 ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
50 // this.name
51 JCTree.JCFieldAccess select = treeMaker.Select(treeMaker.Ident(names.fromString("this")),
52 names.fromString(fieldName));
53 // 生成return代码 return this.name
54 JCTree.JCReturn jcReturn = treeMaker.Return(select);
55 statements.add(jcReturn);
56 // 方法体
57 JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
58 // 生成方法
59 JCTree.JCMethodDecl getNameMethod = treeMaker.MethodDef(
60 treeMaker.Modifiers(Flags.PUBLIC), // 方法修饰符
61 names.fromString("get" + capRename(fieldName)), // 方法名
62 f.vartype, // 方法返回值类型
63 List.nil(),
64 List.nil(), // 方法参数
65 List.nil(),
66 body, // 方法体
67 null
68 );
69 // 插入到语法树中
70 jcClassDecl.defs = jcClassDecl.defs.prepend(getNameMethod);
71}2.6 TreeMaker
2.6.1 Modifiers 修饰符
java中类,方法,字段都是有修饰符的。
1public JCModifiers Modifiers(long flags) {
2 return Modifiers(flags, List.< JCAnnotation >nil());
3}
4
5public JCModifiers Modifiers(long flags,
6 List<JCAnnotation> annotations) {
7 JCModifiers tree = new JCModifiers(flags, annotations);
8 boolean noFlags = (flags & (Flags.ModifierFlags | Flags.ANNOTATION)) == 0;
9 tree.pos = (noFlags && annotations.isEmpty()) ? Position.NOPOS : pos;
10 return tree;
11}- 定义多个修饰符
treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC + Flags.FINAL);
2.6.2 ClassDef 定义类
1
2public JCClassDecl ClassDef(JCModifiers mods, // 访问标志,可以通过 TreeMaker.Modifiers 来创建
3 Name name, // 类名
4 List<JCTypeParameter> typarams, // 泛型参数列表
5 JCExpression extending, // 父类
6 List<JCExpression> implementing, // 实现的接口
7 List<JCTree> defs) { // 类定义的详细语句,包括字段、方法的定义等等
8 JCClassDecl tree = new JCClassDecl(mods,
9 name,
10 typarams,
11 extending,
12 implementing,
13 defs,
14 null);
15 tree.pos = pos;
16 return tree;
17}2.6.3 MethodDef 定义方法
1public JCMethodDecl MethodDef(JCModifiers mods, // mods:访问标志
2 Name name, // 方法名
3 JCExpression restype, // 返回类型,返回类型 restype 填写 null 或者 treeMaker.TypeIdent(TypeTag.VOID) 都代表返回 void 类型
4 List<JCTypeParameter> typarams, // 泛型参数列表
5 List<JCVariableDecl> params, // 参数列表
6 List<JCExpression> thrown, // 异常声明列表
7 JCBlock body, // 方法体
8 JCExpression defaultValue // 默认方法(可能是 interface 中的哪个 default)
9 ) {
10 JCMethodDecl tree = new JCMethodDecl(mods,
11 name,
12 restype,
13 typarams,
14 params,
15 thrown,
16 body,
17 defaultValue,
18 null);
19 tree.pos = pos;
20 return tree;
21}
222.6.4 VarDef 定义字段
1public JCVariableDecl VarDef(JCModifiers mods, // 访问标志
2 Name name, // 参数名称
3 JCExpression vartype, // 类型
4 JCExpression init // 初始化语句
5 ) {
6 JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null);
7 tree.pos = pos;
8 return tree;
9}
10
11public JCVariableDecl VarDef(VarSymbol v,
12 JCExpression init) {
13 return (JCVariableDecl)
14 new JCVariableDecl(
15 Modifiers(v.flags(), Annotations(v.getAnnotationMirrors())),
16 v.name,
17 Type(v.type),
18 init,
19 v).setPos(pos).setType(v.type);
20}2.6.5 Return 定义返回
1public JCReturn Return(JCExpression expr) {
2 JCReturn tree = new JCReturn(expr);
3 tree.pos = pos;
4 return tree;
5}2.6.6 Ident 定义关键字
treeMaker.Ident(names.fromString("this"))
1public JCIdent Ident(Name name) {
2 JCIdent tree = new JCIdent(name, null);
3 tree.pos = pos;
4 return tree;
5}
6
7public JCIdent Ident(Symbol sym) {
8 return (JCIdent)new JCIdent((sym.name != names.empty)
9 ? sym.name
10 : sym.flatName(), sym)
11 .setPos(pos)
12 .setType(sym.type);
13}
14
15public JCExpression Ident(JCVariableDecl param) {
16 return Ident(param.sym);
17}2.6.7 Select
TreeMaker.Select 用于创建域访问 / 方法访问
1public JCFieldAccess Select(JCExpression selected,
2 Name selector)
3{
4 JCFieldAccess tree = new JCFieldAccess(selected, selector, null);
5 tree.pos = pos;
6 return tree;
7}
8
9public JCExpression Select(JCExpression base,
10 Symbol sym) {
11 return new JCFieldAccess(base, sym.name, sym).setPos(pos).setType(sym.type);
12}两个参数第一个是对象,第二个是对象的那个方法。
- this.${fieldName}
1 JCTree.JCFieldAccess aThis = treeMaker.Select(treeMaker.Ident(names.fromString("this")), names.fromString(fieldName));2.6.8 NewClass
用于创建 new 语句语法树节点(JCNewClass)
1public JCNewClass NewClass(JCExpression encl,
2 List<JCExpression> typeargs, // 参数类型列表
3 JCExpression clazz, // 待创建对象的类型
4 List<JCExpression> args, // 参数列表
5 JCClassDecl def) { // 类定义
6 JCNewClass tree = new JCNewClass(encl, typeargs, clazz, args, def);
7 tree.pos = pos;
8 return tree;
9}2.6.9 Assign 赋值语句
类似 Select, 如果说Select是 .
那么Assign是 =
1public JCAssign Assign(JCExpression lhs,
2 JCExpression rhs) {
3 JCAssign tree = new JCAssign(lhs, rhs);
4 tree.pos = pos;
5 return tree;
6}select和assign配合使用
1 // this.MyDate
2 JCTree.JCFieldAccess aThis = treeMaker.Select(treeMaker.Ident(names.fromString("this")), names.fromString(fieldName));
3 // this.MyDate = MyDate;
4 JCTree.JCExpressionStatement exec = treeMaker.Exec(treeMaker.Assign(aThis, treeMaker.Ident(names.fromString(fieldName))));
52.6.10 Exec执行语句
配合赋值语句使用
1public JCExpressionStatement Exec(JCExpression expr) {
2 JCExpressionStatement tree = new JCExpressionStatement(expr);
3 tree.pos = pos;
4 return tree;
5}
62.6.11 Apply 方法调用
1// 创建一个方法调用 user.say("hello world!");
2JCTree.JCExpressionStatement exec2 = treeMaker.Exec(
3 treeMaker.Apply(
4 com.sun.tools.javac.util.List.nil(),
5 treeMaker.Select(
6 treeMaker.Ident(names.fromString("user")), // . 左边的内容
7 names.fromString("say") // . 右边的内容
8 ),
9 com.sun.tools.javac.util.List.of(treeMaker.Literal("hello world!")) // 方法中的内容
10 )
11);2.7 代码片段
2.6的API,是真的难用,这里列几个片段。
2.7.1 生成字段
private String age;
1// 生成参数 例如:private String age;
2treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE), names.fromString("age"), treeMaker.Ident(names.fromString("String")), null);2.7.2 赋值变量
private String name = "西魏陶渊明"
1treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE),names.fromString("name"),treeMaker.Ident(names.fromString("String")),treeMaker.Literal("西魏陶渊明"))
22.7.3 相加
JCTree.Tag.PLUS
1treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString("add")),treeMaker.Binary(JCTree.Tag.PLUS,treeMaker.Literal("a"),treeMaker.Literal("b"))))2.7.4 += 语法
JCTree.Tag.PLUS_ASG
1treeMaker.Exec(treeMaker.Assignop(JCTree.Tag.PLUS_ASG, treeMaker.Ident(names.fromString("add")),
2treeMaker.Literal("test")))2.7.5 ++ 语法
1treeMaker.Exec(treeMaker.Unary(JCTree.Tag.PREINC,treeMaker.Ident(names.fromString("i"))))2.7.6 If语句
1/*
2 创建一个if语句
3 if("BuXueWuShu".equals(name)){
4 add = "a" + "b";
5 }else{
6 add += "test";
7 }
8 */
9// "BuXueWuShu".equals(name)
10JCTree.JCMethodInvocation apply = treeMaker.Apply(
11 com.sun.tools.javac.util.List.nil(),
12 treeMaker.Select(
13 treeMaker.Literal("BuXueWuShu"), // . 左边的内容
14 names.fromString("equals") // . 右边的内容
15 ),
16 com.sun.tools.javac.util.List.of(treeMaker.Ident(names.fromString("name")))
17);
18// add = "a" + "b"
19JCTree.JCExpressionStatement exec3 = treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString("add")), treeMaker.Binary(JCTree.Tag.PLUS, treeMaker.Literal("a"), treeMaker.Literal("b"))));
20// add += "test"
21JCTree.JCExpressionStatement exec1 = treeMaker.Exec(treeMaker.Assignop(JCTree.Tag.PLUS_ASG, treeMaker.Ident(names.fromString("add")), treeMaker.Literal("test")));
22
23JCTree.JCIf anIf = treeMaker.If(
24 apply, // if语句里面的判断语句
25 exec3, // 条件成立的语句
26 exec1 // 条件不成立的语句
27);
28