1. 絮絮叨叨

  • 在学习注解时,自己就有一个疑问:RUNTIME周期的注解,可以通过反射机制让注解生效。如果是SOURCE或者CLASS呢?

  • 比如,在Spring Boot中经常使用@Value注解,为字段设置默认值、从properties读取value初始化字段、读取bean的某个属性以初始化字段

  • 按照我一个菜鸟的理解,一定有一段注解处理代码,可以将@Value注解中的值赋值给对应字段

  • 可能是在对象创建完成后,通过setter方法进行设置;也可能是直接修改源文件,在定义字段时就进行初始化

    @Value("1")
    private int id;
    // 改写后的代码,虽然定义时就进行初始化,更加适合final类型的字段
    private int id = 1;
    
  • 在之后的工作和学习中,接触了一些注解,更加坚定了自己要学习下如何实现一个编译时注解处理器的想法

2. 最原始的方法动态生成Java文件

2.1 文件字符流生成Java文件

  • 很显然,想要实现一个编译时注解处理器,很可能需要修改已有的Java类或者新增Java类

  • 例如,实现支持Builder模式的@Builder注解

    • 如果将Builder类作为一个单独的类,则需要新增Java类。也就是说,需要新建Java文件
    • 如果通过静态方法builder()创建Builder类,则需要修改已有的Java类(lombok的@Builder注解)
  • 最原始的方法,以类似字符串拼接的方式,动态生成Java代码

    import java.io.BufferedWriter;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.nio.charset.StandardCharsets;public class GenerateSourceFile {public static void main(String[] args) {// 获取当前类完全限定名并接解析出包名String name = GenerateSourceFile.class.getCanonicalName();int dotIndex = name.lastIndexOf(".");String packageName = null;if (dotIndex > 0) {packageName = name.substring(0, dotIndex);}// 构建文件路径String classPath = GenerateSourceFile.class.getResource("/").getPath();int index = classPath.indexOf("target/classes/");String path = classPath.substring(0, index) + "src/main/java/";String className = "MyJava";if (packageName == null) {path += className;} else {if (packageName.contains(".")) {path = path + packageName.replaceAll(".", "/") + "/" + className + ".java";} else {path = path + packageName + "/" + className + ".java";}}// 通过字符流,创建java源文件try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), StandardCharsets.UTF_8))) {if (packageName != null) {writer.write("package " + packageName + ";\n\n");}writer.write("public class " + className + " {\n");writer.write("\tprivate String name;\n");writer.write("\tprivate int age;\n");writer.write("}\n");} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
    }
    
  • 我的天啊,写了那么多代码,最后生成的Java文件的代码就这么一点点

    package entity;public class MyJava {private String name;private int age;
    }
    

2.2 令人崩溃的实现方式