java冷知識:javac AbstractProcessor詳解
它可以做什么?
它做的事情當然是生成新類或修改原始的類,比如你遇到這樣的情況下就可以使用:
- 反射好慢,曾見過一個大廠大量是Gson,由于Gson序列化時大量使用了反射,每一個field,每一個get、set都需要用反射,由此帶來了性能問題。解決方法就是使用它盡量減少反射(替換成JSONObject)
- 生成代碼,只要是有注解的地方都可以讀取,總之很多(有些android orm框架)
Processor
javax.annotation.processing.Processor 這個接口將提供注解處理,它遵循SPI規(guī)約進行拓展,jdk默認就有很多處理器的實現(xiàn)。
AbstractProcessor
注解處理器是最重要的拓展處理類了。
注意:請確認JAVA的環(huán)境變量已經(jīng)配置成功,并且把tools.jar(它源于此包)加入到自己電腦的環(huán)境變量中
ProcessingEnvironment | 是一個注解處理工具的集合 |
Element |
是一個接口,表示一個程序元素,它可以是包、類、方法或者一個變量。Element已知的子接口有:![]() PackageElement 表示一個包程序元素。提供對有關(guān)包及其成員的信息的訪問。 ExecutableElement 表示某個類或接口的方法、構(gòu)造方法或初始化程序(靜態(tài)或?qū)嵗?,包括注釋類型元素? TypeElement 表示一個類或接口程序元素。提供對有關(guān)類型及其成員的信息的訪問。注意,枚舉類型是一種類,而注解類型是一種接口。 VariableElement 表示一個字段、enum 常量、方法或構(gòu)造方法參數(shù)、局部變量或異常參數(shù)。 |
源碼
重點關(guān)注process方法
// 源于javax.annotation.processing; public abstract class AbstractProcessor implements Processor { // 集合中指定支持的注解類型的名稱(這里必須時完整的包名+類名) public Set<String> getSupportedAnnotationTypes() { SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class); if (sat == null) { if (isInitialized()) processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "No SupportedAnnotationTypes annotation " + "found on " + this.getClass().getName() + ", returning an empty set."); return Collections.emptySet(); } else return arrayToSet(sat.value()); } // 指定當前正在使用的Java版本 public SourceVersion getSupportedSourceVersion() { SupportedSourceVersion ssv = this.getClass().getAnnotation(SupportedSourceVersion.class); SourceVersion sv = null; if (ssv == null) { sv = SourceVersion.RELEASE_6; if (isInitialized()) processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "No SupportedSourceVersion annotation " + "found on " + this.getClass().getName() + ", returning " + sv + "."); } else sv = ssv.value(); return sv; } // 初始化處理器 public synchronized void init(ProcessingEnvironment processingEnv) { if (initialized) throw new IllegalStateException("Cannot call init more than once."); Objects.requireNonNull(processingEnv, "Tool provided null ProcessingEnvironment"); this.processingEnv = processingEnv; initialized = true; } /** * 這些注解是否由此 Processor 處理,該方法返回ture表示該注解已經(jīng)被處理, 后續(xù)不會再有其他處理器處理; 返回false表示仍可被其他處理器處理 */ public abstract boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv); }
實現(xiàn)一個打印可以API的功能
由于本人是maven環(huán)境,以此展開講
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <!--Disable annotation processing for ourselves--> <!--<compilerArgument>-proc:none</compilerArgument>--> </configuration> </plugin> </plugins> </build>
步驟1:實現(xiàn)一個注解處理器
@Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface ApiAnnotation { String author() default "alex.chen"; String date(); int version() default 1; } @SupportedAnnotationTypes({"com.kxtx.annotation.ApiAnnotation"}) @SupportedSourceVersion(SourceVersion.RELEASE_8) //@AutoService(Processor.class) public class MyProcessor extends AbstractProcessor { //類名的前綴、后綴 public static final String SUFFIX = "AutoGenerate"; public static final String PREFIX = "My_"; @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { Messager messager = processingEnv.getMessager(); for (TypeElement typeElement : annotations) { for (Element e : env.getElementsAnnotatedWith(typeElement)) { //打印 messager.printMessage(Diagnostic.Kind.WARNING, "Printing:" + e.toString()); messager.printMessage(Diagnostic.Kind.WARNING, "Printing:" + e.getSimpleName()); messager.printMessage(Diagnostic.Kind.WARNING, "Printing:" + e.getEnclosedElements().toString()); //獲取注解 ApiAnnotation annotation = e.getAnnotation(ApiAnnotation.class); //獲取元素名并將其首字母大寫 String name = e.getSimpleName().toString(); char c = Character.toUpperCase(name.charAt(0)); name = String.valueOf(c + name.substring(1)); //包裹注解元素的元素, 也就是其父元素, 比如注解了成員變量或者成員函數(shù), 其上層就是該類 Element enclosingElement = e.getEnclosingElement(); //獲取父元素的全類名,用來生成報名 String enclosingQualifiedname; if (enclosingElement instanceof PackageElement) { enclosingQualifiedname = ((PackageElement) enclosingElement).getQualifiedName().toString(); } else { enclosingQualifiedname = ((TypeElement) enclosingElement).getQualifiedName().toString(); } try { //生成包名 String generatePackageName = enclosingQualifiedname.substring(0, enclosingQualifiedname.lastIndexOf(".")); // 生成的類名 String genarateClassName = PREFIX + enclosingElement.getSimpleName() + SUFFIX; //創(chuàng)建Java 文件 JavaFileObject f = processingEnv.getFiler().createSourceFile(genarateClassName); // 在控制臺輸出文件路徑 messager.printMessage(Diagnostic.Kind.WARNING, "Printing: " + f.toUri()); Writer w = f.openWriter(); try { PrintWriter pw = new PrintWriter(w); pw.println("package " + generatePackageName + ";"); pw.println("\npublic class " + genarateClassName + " { "); pw.println("\n /** 打印值 */"); pw.println(" public static void print" + name + "() {"); pw.println(" // 注解的父元素: " + enclosingElement.toString()); pw.println(" System.out.println(\"代碼生成的路徑: " + f.toUri() + "\");"); pw.println(" System.out.println(\"注解的元素: " + e.toString() + "\");"); pw.println(" System.out.println(\"注解的版本: " + annotation.version() + "\");"); pw.println(" System.out.println(\"注解的作者: " + annotation.author() + "\");"); pw.println(" System.out.println(\"注解的日期: " + annotation.date() + "\");"); pw.println(" }"); pw.println("}"); pw.flush(); } finally { w.close(); } } catch (IOException e1) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e1.toString()); } } } return true; } }
步驟2:配置一個spi,在resources目錄新建META-INF/services/javax.annotation.processing.Processor,內(nèi)容為MyProcessor類全名。
步驟3:在另一個項目中使用@ApiAnnotation就會發(fā)現(xiàn)生成了一個新My_feignAutoGenerate.class文件:
public class My_feignAutoGenerate { public My_feignAutoGenerate() { } public static void printStartUp() { System.out.println("代碼生成的路徑: file:/C:/Users/Administrator/Desktop/feign-async-master/target/generated-sources/annotations/My_feignAutoGenerate.java"); System.out.println("注解的元素: com.github.feign.StartUp"); System.out.println("注解的版本: 1"); System.out.println("注解的作者: alex"); System.out.println("注解的日期: 2019-03-6"); } }
到這里基本上已經(jīng)演示完了。
google的 auto-service
<dependency> <groupId>com.google.auto.service</groupId> <artifactId>auto-service</artifactId> <version>1.0-rc2</version> </dependency>
這個類庫非常有用,它非常簡單,使用@AutoService(Processor.class)會基于該接口和注解的類上自動幫我們生成META-INF/services下對應spi文件。它實現(xiàn)的原理就是通過注解處理器。
javapoet
有沒有覺得上面pw.println("package " + generatePackageName + ";");這樣的代碼很痛苦???
JavaPoet is a Java API for generating .java source files.
package com.example.helloworld; public final class HelloWorld { public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); } }
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 helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) .build(); JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); javaFile.writeTo(System.out);
你喜歡的lombok實現(xiàn)原理是怎樣的呢?
lombok(用來幫助開發(fā)人員消除 Java 對象 的冗長),非常好用
里面源碼就不再介紹了??!
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java 微信公眾號開發(fā)相關(guān)總結(jié)
公眾號作為主流的自媒體平臺,有著不少人使用。這次以文本回復作為案例來講解Java相關(guān)的微信公眾號開發(fā)2021-05-05Spring調(diào)度框架EnableScheduling&Scheduled源碼解析
這篇文章主要介紹了Spring調(diào)度框架EnableScheduling&Scheduled源碼解析,@EnableScheduling&Scheduled定時調(diào)度框架,本著不僅知其然還要知其所以然的指導思想,下面對該調(diào)度框架進行源碼解析,以便更好的理解其執(zhí)行過程,需要的朋友可以參考下2024-01-01Java負載均衡算法實現(xiàn)之輪詢和加權(quán)輪詢
網(wǎng)上找了不少負載均衡算法的資源,都不夠全面,后來自己結(jié)合了網(wǎng)上的一些算法實現(xiàn),下面這篇文章主要給大家介紹了關(guān)于Java負載均衡算法實現(xiàn)之輪詢和加權(quán)輪詢的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下2022-04-04springboot 在xml里讀取yml的配置信息的示例代碼
這篇文章主要介紹了springboot 在xml里讀取yml的配置信息的示例代碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09使用restTemplate.postForEntity()的問題
這篇文章主要介紹了使用restTemplate.postForEntity()的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09解析SpringBoot中使用LoadTimeWeaving技術(shù)實現(xiàn)AOP功能
這篇文章主要介紹了SpringBoot中使用LoadTimeWeaving技術(shù)實現(xiàn)AOP功能,AOP面向切面編程,通過為目標類織入切面的方式,實現(xiàn)對目標類功能的增強,本文給大家介紹的非常詳細,需要的朋友可以參考下2022-09-09java 數(shù)據(jù)結(jié)構(gòu) 冒泡排序?qū)崿F(xiàn)代碼
這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu) 冒泡排序的相關(guān)資料,并附實例代碼,有需要的小伙伴可以參考下2016-09-09SpringBoot實現(xiàn)ImportBeanDefinitionRegistrar動態(tài)注入
在閱讀Spring Boot源碼時,看到Spring Boot中大量使用ImportBeanDefinitionRegistrar來實現(xiàn)Bean的動態(tài)注入,它是Spring中一個強大的擴展接口,本文就來詳細的介紹一下如何使用,感興趣的可以了解一下2024-02-02