2013-08-03 1 views
1

Я пытаюсь написать аннотацию Procssor для обнаружения методов, аннотированных аннотацией @PrintMethod. Например, в тестовом классе ниже я хочу напечатать коды в методе тестирования. Есть ли способ сделать это?Пользовательский обработчик аннотации - метод обнаружения с аннотациями

Из класса AnnotationProcessor, указанного ниже, я могу получить имя метода, но не детали метода.

испытаний Класс

public class test { 

    public static void main(String[] args) { 
     System.out.println("Args"); 
    } 

    @PrintMethod 
    private boolean testMethod(String input) { 
     if(input!=null) { 
      return true; 
     } 
     return false; 
    } 
} 

Аннотация Процессор класса

public class AnnotationProcessor extends AbstractProcessor { 
//...... 
    @Override 
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 
     //retrieve test Anntoation 
     Set<? extends Element> ann =roundEnv.getElementsAnnotatedWith(PrintMethod.class); 

     //Print the Method Name 
     for(Element e: ann) { 
      String msg="Element ee :"+ee.getSimpleName().toString(); 
      processingEnv.getMessager().printMessage(javax.tools.Diagnostic.Kind.ERROR, msg, e); 
     } 
    } 
} 

ответ

1

мне было интересно об этом тоже, так что я решил попробовать и понять это. Оказывается, это будет легче, чем я ожидал. Все, что вам нужно сделать, это использовать Trees api из запатентованной библиотеки tools.jar. Я сделал быстрый процессор аннотаций вдоль этих линий здесь: https://github.com/johncarl81/printMethod

Вот мясо его:

@SupportedSourceVersion(SourceVersion.RELEASE_6) 
@SupportedAnnotationTypes("org.printMethod.PrintMethod") 
public class PrintMethodAnnotationProcessor extends AbstractProcessor { 

    private Trees trees; 

    @Override 
    public synchronized void init(ProcessingEnvironment processingEnv) { 
     super.init(processingEnv); 
     trees = Trees.instance(processingEnv); //initialize the Trees api. 
    } 

    @Override 
    public boolean process(Set<? extends TypeElement> typeElements, RoundEnvironment roundEnvironment) { 

     MethodPrintScanner visitor = new MethodPrintScanner(); 

     for (Element e : roundEnvironment.getElementsAnnotatedWith(PrintMethod.class)) { 
      TreePath tp = trees.getPath(e); 
      // visit the annotated methods 
      visitor.scan(tp, trees); 
     } 
     return true; 
    } 

    @Override 
    public SourceVersion getSupportedSourceVersion() { 
     return SourceVersion.latestSupported(); 
    } 
} 

И MethodPrintScanner:

public class MethodPrintScanner extends TreePathScanner { 

    @Override 
    public Object visitMethod(MethodTree methodTree, Object o) { 
     System.out.println(methodTree); 
     return null; 
    } 
} 

Вы можете видеть, что мы можем посетите TreePath, связанный с данным аннотированным элементом. Для каждого метода мы просто println()methodTree, который дает нам содержание метода.

Используя ваш пример, вот результат работы программы во время компиляции:

@PrintMethod() 
private boolean testMethod(String input) { 
    if (input != null) { 
     return true; 
    } 
    return false; 
} 
0

Это одна вещь, чтобы заставить его работать в вашей IDE. Но это другое, чтобы обнаружить их, как только ваш код будет упакован в файлы jar. Следующий код может управлять обоими.

public static List<Class> getPackageClassListHavingAnnotation(String pPackageName, 
                   Class<? extends Annotation> pAnnotation) throws Exception 
{ 
    try 
    { 
    List<Class> classList = getPackageClassList(pPackageName); 
    if ((pAnnotation == null) || (classList == null)) return classList; 

    List<Class> resultList = new ArrayList<Class>(classList.size()); 

    outerLoop: 
    for (Class clazz : classList) 
    { 
     try 
     { 
     for (Method method : clazz.getMethods()) 
     { 
      if (method.isAnnotationPresent(pAnnotation)) 
      { 
      resultList.add(clazz); 
      continue outerLoop; 
      } 
     } 
     } 
     catch (Throwable e) 
     { 
     } 
    } 
    return (resultList.isEmpty()) ? null : resultList; 
    } 
    catch (Exception e) 
    { 
    return null; 
    } 
} 

Это требует следующие вспомогательные методы:

public static List<Class> getPackageClassList(String pPackageName) throws Exception 
{ 
    try 
    { 
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
    String path = pPackageName.replace('.', '/'); 

    List<File> dirs = new ArrayList<File>(); 
    List<JarFile> jars = new ArrayList<JarFile>(); 
    Enumeration<URL> resources = classLoader.getResources(path); 
    if (resources != null) 
    { 
     String fileName; 
     URL resource; 
     File file; 
     while (resources.hasMoreElements()) 
     { 
     resource = resources.nextElement(); 
     fileName = resource.getFile(); 

     if (fileName.contains("!")) 
     { 
      // jar file 
      resource = new URL(StringUtil.getArrayFromString(fileName, "!")[0]); 
      file = urlToFile(resource); 
      if (!file.exists()) continue; 
      jars.add(new JarFile(file)); 
     } 
     else 
     { 
      // class file that is not in a jar file 
      file = urlToFile(resource); 
      if (!file.exists()) continue; 
      dirs.add(file); 
     } 
     } 
    } 

    List<Class> resultList = new ArrayList<Class>(1000); 
    List<Class> tmpClassList; 
    for (File directory : dirs) 
    { 
     tmpClassList = getPckDirClassList(directory, pPackageName); 
     if (tmpClassList != null) resultList.addAll(tmpClassList); 
    } 

    for (JarFile jar : jars) 
    { 
     tmpClassList = getPckJarClassList(jar, pPackageName); 
     if (tmpClassList != null) resultList.addAll(tmpClassList); 
    } 

    return (resultList.isEmpty()) ? null : resultList; 
    } 
    catch (Exception e) 
    { 
    return null; 
    } 
} 

private static List<Class> getPckJarClassList(JarFile pJar, String pPackageName) 
{ 
    if ((pJar == null) || (pPackageName == null)) return null; 

    List<Class> resultList = new ArrayList<Class>(100); 

    Enumeration<JarEntry> jarEntries = (pJar.entries()); 
    JarEntry jarEntry; 
    String fullClassName; 
    while (jarEntries.hasMoreElements()) 
    { 
    jarEntry = jarEntries.nextElement(); 
    fullClassName = jarEntry.getName().replaceAll("/", "."); 
    if (!fullClassName.startsWith(pPackageName)) continue; 
    if (!fullClassName.endsWith(".class")) continue; 

    // do not do a Class.forName for the following path, this can crash the server 
    try 
    { 
     resultList.add(Class.forName(fullClassName.substring(0, fullClassName.length() - 6))); 
    } 
    catch (Throwable e) 
    { 
    } 
    } 

    return (resultList.isEmpty()) ? null : resultList; 
} 

/** 
* Recursive method to find all classes in a package directory tree. 
*/ 
private static List<Class> getPckDirClassList(File pDirectory, String pPackageName) throws ClassNotFoundException 
{ 
    try 
    { 
    if ((pDirectory == null) || (pPackageName == null)) return null; 

    if (!pDirectory.exists()) return null; 
    File[] files = pDirectory.listFiles(); 
    if ((files == null) || (files.length == 0)) return null; 

    List<Class> resultList = new ArrayList<Class>(100); 
    List<Class> tmpClassList; 
    for (File file : files) 
    { 
     if (file.isDirectory()) 
     { 
     tmpClassList = getPckDirClassList(file, pPackageName + "." + file.getName()); 
     if (tmpClassList != null) resultList.addAll(tmpClassList); 
     } 
     else if (file.getName().endsWith(".class")) 
     { 
     try 
     { 
      resultList.add(Class.forName(pPackageName + '.' + file.getName().substring(0, file.getName().length() - 6))); 
     } 
     catch (Throwable e) 
     { 
     } 
     } 
    } 
    return (resultList.isEmpty()) ? null : resultList; 
    } 
    catch (Exception e) 
    { 
    return null; 
    } 
} 

Этот код был проверен с .jar файлами на обоих окнах и систем Unix. Он также был протестирован с .java файлами в IntelliJ на окнах.

 Смежные вопросы

  • Нет связанных вопросов^_^