JavaPoet简介

Poet 诗人。
JavaPoet是一个动态生成代码的开源项目,在某些情况下具有特殊用处。Github地址:https://github.com/square/javapoet
Github上有JavaPoet的官方教程,权威且全面,因为太好了,整篇博客都参考Github上的教程。
首先,我们想用代码生成这个类,且在当前的项目下。

public class Hello {
  public static void main(String[] args) {
    System.out.println("Hello, JavaPoet");
  }
}

用JavaPoet写的话就是这样,没什么需要理解的,熟练掌握API就行。

public class TestOne {
    public static void main(String[] args) throws Exception {
        //构造一个方法
        MethodSpec main = MethodSpec.methodBuilder("main")      //名称
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)         //修饰
                .returns(void.class)                                   //返回
                .addParameter(String[].class, "args")              //参数
                .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet")  //语句
                .build();

        //构造一个类
        TypeSpec hello = TypeSpec.classBuilder("Hello")         //名称
                .addModifiers(Modifier.PUBLIC)                         //修饰
                .addMethod(main)                                        //方法
                .build();

        //生成一个Java文件
        JavaFile javaFile = JavaFile.builder("com.xupt.willscorpio.javatest", hello)
                .build();
        
        //将java写到当前项目中
        javaFile.writeTo(System.out);    //打印到命令行中
        File file = new File("."+"\\src\\");
        if (file.exists()) {
            file.delete();
        }
        javaFile.writeTo(file);
    }
}

这是在运行之前的项目结构。
在这里插入图片描述
这是运行后的项目结构,可以看到的是,在项目中添加了一个Hello类。
在这里插入图片描述
进去看一下,不仅自动导包了,而且也没有报错,和我们预期的一模一样,很完美。
在这里插入图片描述
使用JavaPoet动态生成Java文件的主体就完成了,剩下的就是填充细节,比如添加注解,JavaDoc或者更加复杂的语句。

1)常用类

含义
MethodSpec代表一个构造函数或方法声明
TypeSpec代表一个类,接口,或者枚举声明
FieldSpec代表一个成员变量,一个字段声明
JavaFile包含一个顶级类的Java文件
ParameterSpec用来创建参数
AnnotationSpec用来创建注解
TypeName类型,如在添加返回值类型是使用 TypeName.VOID
ClassName用来包装一个类

2)占位符

占位符含义
$L参数
$S字符串的模板,将指定的字符串替换到$S的地方
$N$N在JavaPoet中代指的是一个名称,例如调用的方法名称,变量名称,这一类存在意思的名称
$T$T 在JavaPoet代指的是TypeName,该模板主要将Class抽象出来,用传入的TypeName指向的Class来代替

基础知识了解之后,剩下的就在代码中理解吧。

3)大括号

void main() {
 int total = 0;
 for (int i = 0; i < 10; i++) {
   total += i;
 }
}

如果现在我们想生成这个函数应该怎么做。

MethodSpec main = MethodSpec.methodBuilder("main")
                .addCode("int total = 0;\n" +
                        "  for (int i = 0; i < 10; i++) {\n" +
                        "    total += i;\n" +
                        "  }")
                .build();

我将函数体全部复制到了addCode()中,虽然简便而且可以达到目的,但是不够灵活,有没有更好的方法。

MethodSpec main = MethodSpec.methodBuilder("main")
                .addStatement("int total = 0")
                .beginControlFlow("for(int i=0;i<10;i++)")
                .addStatement("total +=i;")
                .endControlFlow()
                .build();

这次我们用语句写,beginControlflow() 代表大括号的开始,endControlFlow()则代表大括号的结束,而且中间可以添加语句。但还是不够灵活,我们想把其中的数值变一下,可以吗。

int from = 1;
int to = 100;
String op = "*";
MethodSpec main = MethodSpec.methodBuilder("main")
   			   .addStatement("int total = 0")
               .beginControlFlow("for(int i=" + from + ";i<" + to + ";i++)")
               .addStatement("total = total "+op+ " i;")
               .endControlFlow()
               .build();

现在倒是很灵活,但是可读性也太差了,这时前面的占位符就排上用场了。

	   int from = 1;
       int to = 100;
       String op = "*";
       MethodSpec main = MethodSpec.methodBuilder("main")
               .addStatement("int total = 0")
               .beginControlFlow("for(int i=$L;i<$L;i++)",from,to)
               .addStatement("total = total $L i;",op)
               .endControlFlow()
               .build();

灵活清晰又美观,使用$L代替。

4)占位符的使用

$L在前面说过了,现在看一下$S$S比较简单,字符串占位。

TypeSpec hello = TypeSpec.classBuilder("Hello")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(generateMethod("One"))
                .addMethod(generateMethod("Two"))
                .addMethod(generateMethod("Three"))
                .build();
                
private static MethodSpec generateMethod(String name) {
        MethodSpec methodSpec = MethodSpec.methodBuilder(name)
                .returns(String.class)
                .addStatement("return $S",name)
                .build();
        return methodSpec;
    }

我们返回的数据使用$S占位输出,结果如下。

public class Hello {
  String One() {
    return "One";
  }

  String Two() {
    return "Two";
  }

  String Three() {
    return "Three";
  }
}

那·$S$L有什么区别呢,$S侧重于字符串,而$L侧重于值,我们做下面这样一个实验,将代码改为这样,用$L代替$S

private static MethodSpec generateMethod(String name) {
        MethodSpec methodSpec = MethodSpec.methodBuilder(name)
                .returns(String.class)
                .addStatement("return $L",name)
                .build();
        return methodSpec;
    }

结果如下,报错了。
在这里插入图片描述
那正确的应该是怎样的,改成这样就行了。

private static MethodSpec generateMethod(String name) {
        MethodSpec methodSpec = MethodSpec.methodBuilder(name)
                .returns(String.class)
                .addStatement("return \"$L\"",name)
                .build();
        return methodSpec;
    }

$L$S在前面我们都说过了,接下来就是$T,代表类,我们在前面也用到了这个占位符,System.out.println()中的System我们就用$T进行了占位。那我现在有一个问题,$T可以用$T替换吗?
我们先生成一个Say类,放在其他目录下。
在这里插入图片描述

public static void say() {
        System.out.println("Hello,JavaPoet");
    }
MethodSpec main = MethodSpec.methodBuilder("main")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addParameter(String[].class,"args")
                .addStatement("$L.say()","Say")
                .build();

我们不用$T而是用$L,看一下生成后的结果。
在这里插入图片描述
报红了,因为没有自动导包,那我们用$T代替$L试一下。

MethodSpec main = MethodSpec.methodBuilder("main")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addParameter(String[].class,"args")
                .addStatement("$T.say()", Say.class)
                .build();

在这里插入图片描述
看来用到类的时候,必须要用$T,因为这牵扯到导包的问题,当然,如果在同一目录下,不用导包的情况下,也可以用$L代替$T
对于类型,我们还有另外一种写法。
如果我们现在准备下面这段代码应该怎么做。

public class Hello {
  public static void main(String[] args) {
    List<Say> list = new ArrayList<>();
    list.add(new Say());
    list.add(new Say());
    list.add(new Say());
    System.out.println(list.size());
  }
}
ClassName say = ClassName.get(Say.class);
        ClassName list = ClassName.get(List.class);
        ClassName arrayList = ClassName.get(ArrayList.class);
        TypeName listOfSay = ParameterizedTypeName.get(list, say);

        //构造一个方法
        MethodSpec main = MethodSpec.methodBuilder("main")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addStatement("$T list = new $T()", listOfSay, arrayList)
                .addStatement("list.add(new $T())", say)
                .addStatement("list.add(new $T())", say)
                .addStatement("list.add(new $T())", say)
                .addStatement("System.out.print(list.size())")
                .build();

在这里插入图片描述
可以看到没有出问题,泛型的话我们可以用ParameterizedTypeName这个类来生成,我们也可以用ClassName为类生成别名,便于使用,因为System这个类,没有导包的问题,而且是固定的代码,所以可以直接写成字符串。
接下来是$N$NJavaPoet中代指的是一个名称,例如调用的方法名称,变量名称,这一类存在意思的名称。使用比较广范,也可以与其他占位符替代使用。

public class Hello {
  public static void main(String[] args) {
    System.out.println("Start");
    say("One");
    say("Two");
    say("Three");
    System.out.println("End");
  }
  private static void say(String string) {
    System.out.println(string);
  }
}

在这里我们用$N占位符。

MethodSpec say = MethodSpec.methodBuilder("say")
                .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
                .returns(void.class)
                .addParameter(String.class, "string")
                .addStatement("System.out.println(string)")
                .build();

        MethodSpec main = MethodSpec.methodBuilder("main")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addParameter(String[].class, "args")
                .addStatement("System.out.println(\"Start\")")
                .addStatement("$N(\"One\")",say)
                .addStatement("$N(\"Two\")",say)
                .addStatement("$N(\"Three\")",say)
                .addStatement("System.out.println(\"End\")")
                .build();

        TypeSpec hello = TypeSpec.classBuilder("Hello")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(main)
                .addMethod(say)
                .build();

因为在main方法中调用了say方法,所以要将say放在前面,这样才可以调用。
$N可以被$L等代替使用。

MethodSpec say = MethodSpec.methodBuilder("say")
                .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
                .returns(void.class)
                .addParameter(String.class, "string")
                .addStatement("System.out.println(string)")
                .build();

        MethodSpec main = MethodSpec.methodBuilder("main")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addParameter(String[].class, "args")
                .addStatement("System.out.println(\"Start\")")
                .addStatement("$L(\"One\")","say")
                .addStatement("$L(\"Two\")","say")
                .addStatement("$L(\"Three\")","say")
                .addStatement("System.out.println(\"End\")")
                .build();

        TypeSpec hello = TypeSpec.classBuilder("Hello")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(main)
                .addMethod(say)
                .build();

我们使用$L代替$N来使用,同样可以完成功能。
在这里插入图片描述
常用的已经差不多了,还是继续推荐官方教程。https://github.com/square/javapoet

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android是一个开放的移动操作系统,爱好者和开发者可以根据自己的需求进行定制和开发。在Android开发中,JavaPoet是一个非常有用的库,可以动态生成Java代码,简化一些重复的工作。而AOP(面向切面编程)则是一种编程范式,可以将横切关注点与业务逻辑分离,提高代码的可复用性和可维护性。 动态权限申请是Android开发中经常遇到的一个问题。在Android系统中,一些敏感的操作和资源访问需要动态申请权限,以确保用户的隐私和安全。传统的权限申请方式是在每个需要权限的地方都进行判断和申请,这样会导致代码的冗余和可读性的下降。使用AOP结合JavaPoet可以实现动态权限申请的解决方案。 首先,我们可以通过AOP在需要权限的方法周围添加一个切面,用于检查和申请权限。通过AspectJ等AOP框架,我们可以定义一个切面,在方法执行之前和之后执行相应的逻辑。 然后,利用JavaPoet动态生成申请权限的代码。我们可以定义一个注解,用于标识需要权限的方法。在AOP切面中,当检测到方法上有该注解时,生成相应的权限申请代码。 最后,在代码编译阶段,通过JavaPoet生成的代码会自动插入到原始代码中。这样,我们就可以在运行时动态地进行权限的申请了。 通过以上的实践,我们可以实现动态权限申请的功能,同时可以减少重复的代码,并提高代码的可维护性。使用JavaPoet和AOP相结合的方式,可以使我们的开发变得更加高效和便捷。它们为Android开发带来了更多的灵活性和扩展性,帮助我们更好地应对权限申请的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值