From 36af82031393d5de10821375a786d79e4b17f191 Mon Sep 17 00:00:00 2001 From: daniel Date: Sat, 13 May 2023 21:42:57 +0800 Subject: [PATCH] first commit --- .gitignore | 5 + README.md | 13 ++ annotation-processor/pom.xml | 29 ++++ .../roadl/annoprocessor/BuilderProcessor.java | 136 ++++++++++++++++++ .../roadl/annoprocessor/BuilderProperty.java | 12 ++ annotation-user/pom.xml | 21 +++ .../main/java/com/roadl/annouser/Person.java | 26 ++++ .../roadl/annouser/PersonBuilderUnitTest.java | 15 ++ pom.xml | 45 ++++++ 9 files changed, 302 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 annotation-processor/pom.xml create mode 100644 annotation-processor/src/main/java/com/roadl/annoprocessor/BuilderProcessor.java create mode 100644 annotation-processor/src/main/java/com/roadl/annoprocessor/BuilderProperty.java create mode 100644 annotation-user/pom.xml create mode 100644 annotation-user/src/main/java/com/roadl/annouser/Person.java create mode 100644 annotation-user/src/test/java/com/roadl/annouser/PersonBuilderUnitTest.java create mode 100644 pom.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..705d045 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/annotation-processor/target/ +/annotation-user/target/ +/annotation_processing.iml +/annotation-processor/annotationprocessor.iml +/.idea/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..ad07cf0 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +refer to: https://www.baeldung.com/java-annotation-processing-builder + +# 1 主要演示效果 +在编译期,将 +annotation-user/target/classes/com/roadl/annouser/Person.class + +setter上的注解生成辅助类: +annotation-user/target/classes/com/roadl/annouser/PersonBuilder.class + +# 2 module化 +annotation_processing 父project +annotation_processing/annotation-user 子module A,依赖module B +annotation_processing/annotation-processor 子module B diff --git a/annotation-processor/pom.xml b/annotation-processor/pom.xml new file mode 100644 index 0000000..72654fe --- /dev/null +++ b/annotation-processor/pom.xml @@ -0,0 +1,29 @@ + + + + annotation_processing + com.roadl + 1.0-SNAPSHOT + + 4.0.0 + + annotation-processor + + + 1.0-rc2 + + + + + + com.google.auto.service + auto-service + ${auto-service.version} + provided + + + + + \ No newline at end of file diff --git a/annotation-processor/src/main/java/com/roadl/annoprocessor/BuilderProcessor.java b/annotation-processor/src/main/java/com/roadl/annoprocessor/BuilderProcessor.java new file mode 100644 index 0000000..22850ea --- /dev/null +++ b/annotation-processor/src/main/java/com/roadl/annoprocessor/BuilderProcessor.java @@ -0,0 +1,136 @@ +package com.roadl.annoprocessor; + +import com.google.auto.service.AutoService; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.ExecutableType; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; + +@SupportedAnnotationTypes("com.roadl.annoprocessor.BuilderProperty") +@SupportedSourceVersion(SourceVersion.RELEASE_8) +@AutoService(Processor.class) +public class BuilderProcessor extends AbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (TypeElement annotation : annotations) { + Set annotatedElements = roundEnv.getElementsAnnotatedWith(annotation); + + Map> annotatedMethods = annotatedElements.stream().collect( + Collectors.partitioningBy(element -> + ((ExecutableType) element.asType()).getParameterTypes().size() == 1 + && element.getSimpleName().toString().startsWith("set"))); + + List setters = annotatedMethods.get(true); + List otherMethods = annotatedMethods.get(false); + + otherMethods.forEach(element -> + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "@com.roadl.annoprocessor.BuilderProperty must be applied to a setXxx method " + + "with a single argument", element)); + + if (setters.isEmpty()) { + continue; + } + String className = ((TypeElement) setters.get(0) + .getEnclosingElement()).getQualifiedName().toString(); + + Map setterMap = setters.stream().collect(Collectors.toMap( + setter -> setter.getSimpleName().toString(), + setter -> ((ExecutableType) setter.asType()) + .getParameterTypes().get(0).toString() + )); + + try { + writeBuilderFile(className, setterMap); + } catch (IOException e) { + e.printStackTrace(); + } + } + return true; + } + + private void writeBuilderFile( + String className, Map setterMap) + throws IOException { + + String packageName = null; + int lastDot = className.lastIndexOf('.'); + if (lastDot > 0) { + packageName = className.substring(0, lastDot); + } + + String simpleClassName = className.substring(lastDot + 1); + String builderClassName = className + "Builder"; + String builderSimpleClassName = builderClassName + .substring(lastDot + 1); + + JavaFileObject builderFile = processingEnv.getFiler() + .createSourceFile(builderClassName); + + try (PrintWriter out = new PrintWriter(builderFile.openWriter())) { + + if (packageName != null) { + out.print("package "); + out.print(packageName); + out.println(";"); + out.println(); + } + + out.print("public class "); + out.print(builderSimpleClassName); + out.println(" {"); + out.println(); + + out.print(" private "); + out.print(simpleClassName); + out.print(" object = new "); + out.print(simpleClassName); + out.println("();"); + out.println(); + + out.print(" public "); + out.print(simpleClassName); + out.println(" build() {"); + out.println(" return object;"); + out.println(" }"); + out.println(); + + setterMap.entrySet().forEach(setter -> { + String methodName = setter.getKey(); + String argumentType = setter.getValue(); + + out.print(" public "); + out.print(builderSimpleClassName); + out.print(" "); + out.print(methodName); + + out.print("("); + + out.print(argumentType); + out.println(" value) {"); + out.print(" object."); + out.print(methodName); + out.println("(value);"); + out.println(" return this;"); + out.println(" }"); + out.println(); + }); + + out.println("}"); + } + } +} diff --git a/annotation-processor/src/main/java/com/roadl/annoprocessor/BuilderProperty.java b/annotation-processor/src/main/java/com/roadl/annoprocessor/BuilderProperty.java new file mode 100644 index 0000000..eb20681 --- /dev/null +++ b/annotation-processor/src/main/java/com/roadl/annoprocessor/BuilderProperty.java @@ -0,0 +1,12 @@ +package com.roadl.annoprocessor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface BuilderProperty { + +} diff --git a/annotation-user/pom.xml b/annotation-user/pom.xml new file mode 100644 index 0000000..bb4b5ea --- /dev/null +++ b/annotation-user/pom.xml @@ -0,0 +1,21 @@ + + + + annotation_processing + com.roadl + 1.0-SNAPSHOT + + 4.0.0 + + annotation-user + + + + com.roadl + annotation-processor + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/annotation-user/src/main/java/com/roadl/annouser/Person.java b/annotation-user/src/main/java/com/roadl/annouser/Person.java new file mode 100644 index 0000000..af93221 --- /dev/null +++ b/annotation-user/src/main/java/com/roadl/annouser/Person.java @@ -0,0 +1,26 @@ +package com.roadl.annouser; + +import com.roadl.annoprocessor.BuilderProperty; + +public class Person { + private int age; + private String name; + + + public int getAge() { + return age; + } + + @BuilderProperty + public void setAge(int age) { + this.age = age; + } + + public String getName() { + return name; + } + @BuilderProperty + public void setName(String name) { + this.name = name; + } +} diff --git a/annotation-user/src/test/java/com/roadl/annouser/PersonBuilderUnitTest.java b/annotation-user/src/test/java/com/roadl/annouser/PersonBuilderUnitTest.java new file mode 100644 index 0000000..914f300 --- /dev/null +++ b/annotation-user/src/test/java/com/roadl/annouser/PersonBuilderUnitTest.java @@ -0,0 +1,15 @@ +package com.roadl.annouser; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +public class PersonBuilderUnitTest { + @Test + public void whenBuildPersonWithBuilder_thenObjectHasPropertyValues() { + + Person person = new PersonBuilder().setAge(25).setName("John").build(); + + assertEquals(25, person.getAge()); + assertEquals("John", person.getName()); + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a5f3b30 --- /dev/null +++ b/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + com.roadl + annotation_processing + pom + 1.0-SNAPSHOT + + + annotation-processor + annotation-user + + + + + + junit + junit + 4.12 + test + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + \ No newline at end of file