在运行时扫描Java注释

如何搜索带注释类的整个类路径?

我正在创建一个库,希望允许用户注释他们的类,因此当Web应用程序启动时,我需要扫描整个类路径以查找某些注释。

我正在考虑类似于Java EE 5 Web服务或EJB的新功能。你用@WebService@EJB注释你的类,系统会在加载时找到这些类,这样它们就可以远程访问。

209357 次浏览

Classloader API没有“枚举”方法,因为类加载是一个“按需”活动——在类路径中通常有数千个类,其中只有一小部分是需要的(目前仅rt.jar就有48MB !)

所以,即使你可以枚举所有的类,这将是非常耗时和内存消耗。

简单的方法是在设置文件(xml或任何适合你的文件)中列出相关的类;如果您希望自动执行此操作,请将自己限制在一个JAR或一个类目录中。

使用org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider

API

从基本包扫描类路径的组件提供程序。然后,它对结果类应用排除和包括过滤器来查找候选类。

ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(<DO_YOU_WANT_TO_USE_DEFALT_FILTER>);


scanner.addIncludeFilter(new AnnotationTypeFilter(<TYPE_YOUR_ANNOTATION_HERE>.class));


for (BeanDefinition bd : scanner.findCandidateComponents(<TYPE_YOUR_BASE_PACKAGE_HERE>))
System.out.println(bd.getBeanClassName());

另一个解是ronmamo的 反射

快速回顾一下:

  • 如果您正在使用Spring,那么Spring解决方案是最好的选择。否则就是很大的依赖。
  • 直接使用ASM有点麻烦。
  • 直接使用Java Assist也很笨拙。
  • 注释超轻,方便。还没有专家集成。
  • Reflections索引所有东西,而且非常快。

如果你想要一个真正的重量轻(没有依赖,简单的API, 15 kb的jar文件)和非常快解决方案,请查看https://github.com/rmuller/infomas-asl中找到的annotation-detector

声明:我是作者。

您可以使用Java Pluggable Annotation Processing API编写注释处理器,该处理器将在编译过程中执行,并将收集所有注释类并构建索引文件以供运行时使用。

这是可能进行带注释类发现的最快方式,因为您不需要在运行时扫描类路径,这通常是非常缓慢的操作。此外,这种方法适用于任何类加载器,而不仅仅适用于运行时扫描程序通常支持的urlclassloader。

上面的机制已经在ClassIndex库中实现。

使用@IndexAnnotated元注释注释自定义注释。这将在编译时创建一个索引文件:META-INF/annotations/com/test/YourCustomAnnotation,列出所有带注释的类。你可以在运行时通过执行以下命令访问索引:

ClassIndex.getAnnotated(com.test.YourCustomAnnotation.class)

谷歌反射似乎比Spring快得多。找到解决此差异的特性请求:http://www.opensaga.org/jira/browse/OS-738

这就是为什么在开发过程中使用reflection作为应用程序的启动时间是非常重要的。对于我的用例来说,Reflections似乎也很容易使用(找到一个接口的所有实现者)。

你可以用ClassGraph找到带有任何给定注释的类,也可以搜索其他感兴趣的标准,例如实现给定接口的类。(免责声明,我是ClassGraph的作者。)ClassGraph可以在内存中为类路径上的所有类或白名单包中的类构建整个类图(所有类、注释、方法、方法参数和字段)的抽象表示,并且您可以任意查询该类图。ClassGraph支持更多的类路径规范机制和类加载器比任何其他扫描器都多,并且还可以与新的JPMS模块系统无缝工作,所以如果你的代码基于ClassGraph,你的代码将最大限度地可移植。请在这里查看API。

在Spring中,您还可以使用AnnotationUtils类编写以下代码。例如:

Class<?> clazz = AnnotationUtils.findAnnotationDeclaringClass(Target.class, null);

要了解更多细节和所有不同的方法,请查看官方文档: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AnnotationUtils.html < / p >

现在回答还来得及吗? 我会说,最好使用ClassPathScanningCandidateComponentProviderScannotations

这样的库

但即使有人想用classLoader来尝试一下,我也自己写了一些来打印包中类的注释:

public class ElementScanner {


public void scanElements(){
try {
//Get the package name from configuration file
String packageName = readConfig();


//Load the classLoader which loads this class.
ClassLoader classLoader = getClass().getClassLoader();


//Change the package structure to directory structure
String packagePath  = packageName.replace('.', '/');
URL urls = classLoader.getResource(packagePath);


//Get all the class files in the specified URL Path.
File folder = new File(urls.getPath());
File[] classes = folder.listFiles();


int size = classes.length;
List<Class<?>> classList = new ArrayList<Class<?>>();


for(int i=0;i<size;i++){
int index = classes[i].getName().indexOf(".");
String className = classes[i].getName().substring(0, index);
String classNamePath = packageName+"."+className;
Class<?> repoClass;
repoClass = Class.forName(classNamePath);
Annotation[] annotations = repoClass.getAnnotations();
for(int j =0;j<annotations.length;j++){
System.out.println("Annotation in class "+repoClass.getName()+ " is "+annotations[j].annotationType().getName());
}
classList.add(repoClass);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}


/**
* Unmarshall the configuration file
* @return
*/
public String readConfig(){
try{
URL url = getClass().getClassLoader().getResource("WEB-INF/config.xml");
JAXBContext jContext = JAXBContext.newInstance(RepositoryConfig.class);
Unmarshaller um =  jContext.createUnmarshaller();
RepositoryConfig rc = (RepositoryConfig) um.unmarshal(new File(url.getFile()));
return rc.getRepository().getPackageName();
}catch(Exception e){
e.printStackTrace();
}
return null;


}
}

在配置文件中,放入包名并将其解编到一个类。

如果你正在寻找反射的替代品,我想推荐Panda Utilities - AnnotationsScanner。这是一个基于反射库源代码的无Guava (Guava有~3MB, Panda Utilities有~200kb)扫描仪。

它还专门用于基于未来的搜索。如果你想扫描多次包含的源,或者甚至提供一个API,允许某人扫描当前的类路径,AnnotationsScannerProcess缓存所有获取的ClassFiles,所以它真的很快。

AnnotationsScanner用法的简单例子:

AnnotationsScanner scanner = AnnotationsScanner.createScanner()
.includeSources(ExampleApplication.class)
.build();


AnnotationsScannerProcess process = scanner.createWorker()
.addDefaultProjectFilters("net.dzikoysk")
.fetch();


Set<Class<?>> classes = process.createSelector()
.selectTypesAnnotatedWith(AnnotationTest.class);

谷歌反射如果你也想发现接口。

Spring ClassPathScanningCandidateComponentProvider没有发现接口。

Spring有一个AnnotatedTypeScanner类。< br > 该类内部使用

ClassPathScanningCandidateComponentProvider

这个类有实际扫描类路径资源的代码。它通过使用运行时可用的类元数据来做到这一点

可以简单地扩展这个类或使用相同的类进行扫描。下面是构造函数定义。

/**
* Creates a new {@link AnnotatedTypeScanner} for the given annotation types.
*
* @param considerInterfaces whether to consider interfaces as well.
* @param annotationTypes the annotations to scan for.
*/
public AnnotatedTypeScanner(boolean considerInterfaces, Class<? extends Annotation>... annotationTypes) {


this.annotationTypess = Arrays.asList(annotationTypes);
this.considerInterfaces = considerInterfaces;
}

扎普有一个很好的注释,它包含了所有这些答案:

new Reflections("my.package").getTypesAnnotatedWith(MyAnnotation.class)