我正在尝试编写一个注释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);
}
}
}
答案 0 :(得分:1)
我对此也很好奇,所以我决定尝试解决这个问题。结果比我想象的要容易。您需要做的就是利用专有tools.jar库中的Trees
api。我在这里按照以下几行制作了一个快速注释处理器: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;
}
答案 1 :(得分: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;
}
}
此代码已在Windows和Unix系统上使用.jar
文件进行了测试。它还在Windows上的IntelliJ中使用.java
文件进行了测试。