java-apt程式設計實踐(Annotatino Processing Tool+maven)

自在现实發表於2024-06-25

背景
最近在研究mapStruct,而mapStruct核心技巧就是apt,透過編譯期註解+freemarker進行java檔案生成,從而省去了很多編碼。
本wiki將闡述一個apt的程式設計實踐。

實踐過程
建立一個hello工程
工程由兩個模組組成:

application模組,將使用自定義的編譯器註解
apt模組,自定義註解,並且完成AbstractProcessor的繼承和實現
根pom如下:


4.0.0

<groupId>com.mine</groupId>
<artifactId>apt-test</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
    <module>application</module>
    <module>apt</module>
</modules>
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
</properties>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
application的pom如下



apt-test
com.mine
1.0-SNAPSHOT

4.0.0

<artifactId>application</artifactId>
<dependencies>
    <dependency>
        <groupId>com.mine</groupId>
        <artifactId>apt</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apt的pom如下



apt-test
com.mine
1.0-SNAPSHOT

4.0.0

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>

<artifactId>apt</artifactId>
<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.5.1</version>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
                <compilerArgument>-proc:none</compilerArgument>
            </configuration>
        </plugin>
    </plugins>
</build>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

建立一個Main類,並且執行一下看看結果

編寫apt程式碼
自定義一個註解

實現AbstractProcessor,實現process過程

package com.mine.processor;

import com.mine.anno.CustomAnnotation;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.util.Set;

@SupportedAnnotationTypes({
"com.mine.anno.CustomAnnotation"
})
public class MyProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//然後檔案會被解釋為.class檔案

    StringBuilder builder = new StringBuilder()
            .append("package com.mine.annotationprocessor.generated;\n\n")
            .append("public class GeneratedClass {\n\n")
            .append("\tpublic String getMessage() {\n")
            .append("\t\treturn \"");

    //獲取所有被CustomAnnotation修飾的程式碼元素
    for (Element element : roundEnv.getElementsAnnotatedWith(CustomAnnotation.class)) {
        String objectType = element.getSimpleName().toString();
        builder.append(objectType).append(" exists!\\n");
    }

    builder.append("\";\n")
            .append("\t}\n")
            .append("}\n");

    try {
        JavaFileObject source = processingEnv.getFiler().createSourceFile(
                "com.mine.annotationprocessor.generated.GeneratedClass");

        Writer writer = source.openWriter();
        writer.write(builder.toString());
        writer.flush();
        writer.close();
    } catch (IOException e) {
        //
    }
    return false;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
過程很簡單,透過StringBuilder來寫一個java檔案

配置resources
在resources/META-INF/services/建立javax.annotation.processing.Processor
內容為:
com.mine.processor.MyProcessor

這裡表示,java compile編譯器會透過配置檔案去找到對應的processor,進行編譯期的呼叫

編寫application程式碼
建立一個DemoClass類

package com.mine;

import com.mine.anno.CustomAnnotation;

// 使用我們自定義的註解
@CustomAnnotation
public class DemoClass {

}
1
2
3
4
5
6
7
8
9
建立一個MyModel類

package com.mine;

import lombok.Data;

// 使用lombok的Data,這裡也可以自動生成程式碼
@Data
public class MyModel {
private String name;
private Integer age;
}
1
2
3
4
5
6
7
8
9
10
編譯
在根目錄執行
mvn compile
或者直接使用idea執行

檢視class位元組碼檔案的生成情況

可以看到已經有我們生成的程式碼了

MyModel使用了lombok,可以自動生成get set和toString等方法

評價
透過apt可以生成很多方便的程式碼生成模板,比如mapStruct,lombok等。可以參考博主的其他部落格檢視mapStruct的使用。
————————————————

                        版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。

原文連結:https://blog.csdn.net/wangjie5540/article/details/105365717

相關文章