溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JDK6.0的新特性之六:插入式注解處理API(Pluggable Annotation Processing API)

發(fā)布時(shí)間:2020-08-17 17:04:02 來(lái)源:ITPUB博客 閱讀:188 作者:okone96 欄目:編程語(yǔ)言
作者: 飛翼 發(fā)表日期: 2007-01-03 20:10

插入式注解處理API(JSR 269)提供一套標(biāo)準(zhǔn)API來(lái)處理Annotations(JSR 175),實(shí)際上JSR 269不僅僅用來(lái)處理Annotation,我覺得更強(qiáng)大的功能是它建立了Java 語(yǔ)言本身的一個(gè)模型,它把method, package, constructor, type, variable, enum, annotation等Java語(yǔ)言元素映射為Types和Elements(兩者有什么區(qū)別?), 從而將Java語(yǔ)言的語(yǔ)義映射成為對(duì)象, 我們可以在javax.lang.model包下面可以看到這些類. 所以我們可以利用JSR 269提供的API來(lái)構(gòu)建一個(gè)功能豐富的元編程(metaprogramming)環(huán)境. JSR 269用Annotation Processor在編譯期間而不是運(yùn)行期間處理Annotation, Annotation Processor相當(dāng)于編譯器的一個(gè)插件,所以稱為插入式注解處理.如果Annotation Processor處理Annotation時(shí)(執(zhí)行process方法)產(chǎn)生了新的Java代碼,編譯器會(huì)再調(diào)用一次Annotation Processor,如果第二次處理還有新代碼產(chǎn)生,就會(huì)接著調(diào)用Annotation Processor,直到?jīng)]有新代碼產(chǎn)生為止.每執(zhí)行一次process()方法被稱為一個(gè)"round",這樣整個(gè)Annotation processing過(guò)程可以看作是一個(gè)round的序列. JSR 269主要被設(shè)計(jì)成為針對(duì)Tools或者容器的API. 舉個(gè)例子,我們想建立一套基于Annotation的單元測(cè)試框架(如TestNG),在測(cè)試類里面用Annotation來(lái)標(biāo)識(shí)測(cè)試期間需要執(zhí)行的測(cè)試方法,如下所示:

@TestMethod
public void testCheckName(){
//do something here
}

這時(shí)我們就可以用JSR 269提供的API來(lái)處理測(cè)試類,根據(jù)Annotation提取出需要執(zhí)行的測(cè)試方法.

另一個(gè)例子是如果我們出于某種原因需要自行開發(fā)一個(gè)符合Java EE 5.0的Application Server(當(dāng)然不建議這樣做),我們就必須處理Common Annotations(JSR 250),Web Services Metadata(JSR 181)等規(guī)范的Annotations,這時(shí)可以用JSR 269提供的API來(lái)處理這些Annotations. 在現(xiàn)在的開發(fā)工具里面,Eclipse 3.3承諾將支持JSR 269

下面我用代碼演示如何來(lái)用JSR 269提供的API來(lái)處理Annotations和讀取Java源文件的元數(shù)據(jù)(metadata)

/**
* Created by IntelliJ IDEA.
* User: Chinajash
* Date: Dec 31, 2006
*/

@SupportedAnnotationTypes("PluggableAPT.ToBeTested")//可以用"*"表示支持所有Annotations@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class MyAnnotationProcessor extends AbstractProcessor {
private void note(String msg) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, msg);
}
public boolean process(Set annotations, RoundEnvironment roundEnv) {
//annotations的值是通過(guò)@SupportedAnnotationTypes聲明的且目標(biāo)源代碼擁有的所有Annotations
for(TypeElement te:annotations){
note("annotation:"+te.toString());
}
Set elements = roundEnv.getRootElements();//獲取源代碼的映射對(duì)象 for(Element e:elements){
//獲取源代碼對(duì)象的成員 List enclosedElems = e.getEnclosedElements();
//留下方法成員,過(guò)濾掉其他成員
List ees = ElementFilter.methodsIn(enclosedElems);
for(ExecutableElement ee:ees){
note("--ExecutableElement name is "+ee.getSimpleName());
List as = ee.getAnnotationMirrors();//獲取方法的Annotations
note("--as="+as);
for(AnnotationMirror am:as){
//獲取Annotation的值
Map map= am.getElementValues();
Set ks = map.keySet();
for(ExecutableElement k:ks){//打印Annotation的每個(gè)值 AnnotationValue av = map.get(k);
note("----"+ee.getSimpleName()+"."+k.getSimpleName()+"="+av.getValue());
}
}
}
}
return false;
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface ToBeTested{
String owner() default "Chinajash";
String group();
}

編譯以上代碼,然后再創(chuàng)建下面的Testing對(duì)象,不要編譯Testing對(duì)象,我在后面會(huì)編譯它

public class Testing{
@ToBeTested(group="A")
public void m1(){
}
@ToBeTested(group="B",owner="QQ")
public void m2(){
}
@PostConstruct//Common Annotation里面的一個(gè)Annotation
public void m3(){
}
}

下面我用以下命令編譯Testing對(duì)象

javac -XprintRounds -processor PluggableAPT.MyAnnotationProcessor Testing.java

-XprintRounds表示打印round的次數(shù),運(yùn)行上面命令后在控制臺(tái)會(huì)看到如下輸出:

Round 1:
input files: {PluggableAPT.Testing}
annotations: [PluggableAPT.ToBeTested, javax.annotation.PostConstruct]
last round: false
Note: annotation:PluggableAPT.ToBeTested
Note: --ExecutableElement name is m1
Note:
--as=@PluggableAPT.ToBeTested(group="A")
Note: ----m1.group=A
Note: --ExecutableElement name is m2
Note:
--as=@PluggableAPT.ToBeTested(group="B", owner="QQ")
Note: ----m2.group=B
Note: ----m2.owner=QQ
Note: --ExecutableElement name is m3
Note:
--as=@javax.annotation.PostConstruct
Round 2:
input files: {}
annotations: []
last round: true

本來(lái)想用JDK6.0的Compiler API來(lái)執(zhí)行上面編譯命令,可是好像現(xiàn)在Compiler API還不支持-processor參數(shù),運(yùn)行時(shí)總報(bào)以下錯(cuò)誤

Exception in thread "main" java.lang.IllegalArgumentException: invalid flag: -processor PluggableAPT.MyAnnotationProcessor

調(diào)用Compiler API的代碼是這樣的

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable sourcefiles = fileManager.getJavaFileObjects("Testing.java");
Set options = new HashSet();
options.add("-processor PluggableAPT.MyAnnotationProcessor");
compiler.getTask(null, fileManager, null, options, null, sourcefiles).call();

不知道這是不是Compiler API的一個(gè)bug.

[@more@]
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI