(四) 源码级注解处理
注解的用处之一就是自动生成包含程序额外信息的"附文件"。Java EE 5使用注解极大地简化了编程模型。
源码级注解是将注解处理器添加到Java编译器中。
例
javac -processor ProcessorClassName1,ProcessorClassName2,... sourceFiles
?
有编译器定位源代码中的注解,然后选择恰当的注解处理器。每个注解处理器会依次执行。如果某哥哥注解处理器创建了一个新的源文件,那么将重复执行这个处理过程。如果某次处理循环没有再产生任何新的源文件,那么就编译所有的源文件。
关于处理器需要指示注释处理器支持哪些注释类型的注释和注释处理器所支持的最新源版本的注释。
例
@SupportedAnnotationTypes("annotation.Property") @SupportedSourceVersion(SourceVersion.RELEASE_6)?通过一个简单示例来演示这项技术,一个DEMO,可以自动产生bean信息类。可以使用一个注解来标记bean的属性,然后运行某个工具对这个源文件进行解析、分析其注解,最后输出bean信息类的源文件。
?
package com.shaogq.annotation.source;import java.beans.Introspector;import java.io.IOException;import java.io.PrintWriter;import java.util.LinkedHashMap;import java.util.Map;import java.util.Set;import javax.annotation.processing.AbstractProcessor;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.tools.Diagnostic.Kind;import javax.tools.JavaFileObject;@SupportedAnnotationTypes("com.shaogq.annotation.Property")@SupportedSourceVersion(SourceVersion.RELEASE_6)public class BeanInfoAnnotationFactory extends AbstractProcessor{ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for(TypeElement t : annotations){ Map<String, Property> props = new LinkedHashMap<String, Property>(); String beanClassName = null; for(Element e:roundEnv.getElementsAnnotatedWith(t)){ String mname = e.getSimpleName().toString(); String[] prefixes = {"get", "set" , "is"}; boolean found = false; for(int i=0;!found && i<prefixes.length;i++){ if(mname.startsWith(prefixes[i])){ found = true; int start = prefixes[i].length(); String name = Introspector.decapitalize(mname.substring(start)); props.put(name, e.getAnnotation(Property.class)); } } if(!found){ processingEnv.getMessager().printMessage(Kind.ERROR, "&Property must be applied to getXxx, setXxx, or isXxx method", e); }else if(beanClassName==null){ beanClassName = ((TypeElement)e.getEnclosingElement()).getQualifiedName().toString(); } } try{ if(beanClassName != null){ writeBeanInfoFile(beanClassName, props); } }catch(Exception e){ e.printStackTrace(); } } return true; } private void writeBeanInfoFile(String beanClassName, Map<String, Property> props) throws IOException{ JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile( beanClassName + "BeanInfo"); PrintWriter out = new PrintWriter(sourceFile.openWriter()); int i = beanClassName.lastIndexOf("."); if(i>0){ out.print("package "); out.print(beanClassName.substring(0, i)); out.print(";"); } out.print("public class "); out.print(beanClassName.substring(i + 1)); out.println("BeanInfo extends java.beans.SimpleBeanInfo"); out.print("{"); out.print(" public java.beans.PropertyDescriptor[] getPropertyDescriptors(){"); out.println(" try{"); for(Map.Entry<String, Property> e :props.entrySet()){ out.print(" java.beans.PropertyDescriptor"); out.print(e.getKey()); out.println("Descriptor"); out.print(" = new java.beans.PropertyDescriptory(""); out.print(e.getKey()); out.print("", "); out.print(beanClassName); out.println(".class);"); String ed = e.getValue().editor().toString(); if(!ed.equals("")){ out.print(" "); out.print(e.getKey()); out.print("Descriptor.setPropertyEditorClass("); out.print(ed); out.println(".class);"); } } out.println(" return new java.beans.PropertyDescriptor[]{"); boolean first = true; for(String p : props.keySet()){ if(first){ first = false; }else{ out.print(","); } out.println(); out.print(" "); out.print(p); out.print("Descriptor"); } out.println(); out.println(" };"); out.println(" }"); out.println(" catch(java.beans.IntrospectionException e){"); out.println(" e.printStackTrace();"); out.println(" return null;"); out.println(" }"); out.println(" }"); out.println("}"); out.close(); } }??
?