在运行时扫描Java注释
search带注释类的整个类path的最佳方法是什么?
我在做一个库,我想允许用户注释他们的类,所以当Web应用程序启动时,我需要扫描整个类path的特定注释。
你知道一个图书馆或Java设施来做到这一点吗?
编辑:我正在考虑像Java EE 5 Web服务或EJB的新function。 您使用@WebService
或@EJB
注释您的类,系统在加载时查找这些类,以便它们可以远程访问。
使用org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
API
从基础包扫描类path的组件提供者。 然后它应用排除和包括filter到结果类查找候选人。
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());
另一个解决scheme是Google的反思 。
快速复审:
- 如果你使用Spring,Spring解决scheme就是要走的路。 否则,这是一个很大的依赖性。
- 直接使用ASM有点麻烦。
- 直接使用Java Assist也很笨重。
- Annovention是超轻量级和方便的。 没有maven集成呢。
- 谷歌思考拉动谷歌collections。 索引一切,然后是超级快。
我刚刚发布了一个超级快速和轻量级的类path扫描器(git repo here) ,它不调用类加载器来加载类path上的类,以便确定子类,超类,注释等,而是直接读取类文件二进制头文件通过,但比rmueller的类path扫描程序更简单,链接在另一个评论)。
我的类path扫描程序可以在类path中find扩展给定超类,实现给定接口或具有给定类标注的类,并且可以在path匹配给定正则expression式的任何types的类path中find文件。
这里是一个使用的例子:
new FastClasspathScanner(new String[] { "com.xyz.widget", "com.xyz.gizmo" }) // Whitelisted package prefixes .matchSubclassesOf(DBModel.class, // c is a subclass of DBModel c -> System.out.println("Found subclass of DBModel: " + c.getName())) .matchClassesImplementing(Runnable.class, // c is a class that implements Runnable c -> System.out.println("Found Runnable: " + c.getName())) .matchClassesWithAnnotation(RestHandler.class, // c is a class annotated with @RestHandler c -> System.out.println("Found RestHandler annotation on class: " + c.getName())) .matchFilenamePattern("^template/.*\\.html", // templatePath is a path on the classpath that matches the above pattern; // inputStream is a stream opened on the file or zipfile entry. // No need to close inputStream before exiting, it is closed by caller. (templatePath, inputStream) -> { try { String template = IOUtils.toString(inputStream, "UTF-8"); System.out.println("Found template: " + absolutePath + " (size " + template.length() + ")"); } catch (IOException e) { throw new RuntimeException(e); } }) .scan(); // Actually perform the scan
扫描程序还会logging遇到的任何文件或目录的最新上次修改时间戳记,您可以通过调用以下命令查看最新的上次修改时间戳记是否增加了(指示类path上的某些内容已更新)
boolean classpathContentsModified = fastClassPathScanner.classpathContentsModifiedSinceScan();
如果类path上的某些内容被更新,这可用于启用dynamic类重新加载,例如,以支持在Web服务器中热replace路由处理程序类。 上面的调用比原来的scan()调用快几倍,因为只需要修改时间戳。
如果你想要一个真正的轻量级 (没有依赖关系,简单的API,15 kb的jar文件)和非常快速的解决scheme,请看https://github.com/rmuller/infomas-asl上的;annotation-detector
免责声明:我是作者。
您可以使用Java Pluggable Annotation Processing API来编写将在编译过程中执行的注释处理器,并将收集所有注释的类并构build索引文件以供运行时使用。
这是执行注释类发现的最快方式,因为您不需要在运行时扫描类path,这通常是非常慢的操作。 此方法也适用于任何类加载器,不仅适用于运行时扫描程序通常支持的URLClassLoader。
上述机制已经在ClassIndex库中实现了。
要使用它@IndexAnnotated批注注释您的自定义注释。 这将在编译时创build一个索引文件:META-INF / annotations / com / test / YourCustomAnnotation列出所有带注释的类。 您可以在运行时通过执行以下命令来访问索引:
ClassIndex.getAnnotated(com.test.YourCustomAnnotation.class)
使用ServiceLoader ,或者如果您不在Java 6中,请实现自己的 。
也许注释处理器可以在编译时在META-INF / services下产生必要的文件。
尝试扫描 。
它可用于search类path或Web应用程序lib目录中的特定注释。
稍微偏离主题,但是Spring也使用<context:component-scan>来做类似的事情,你也许可以研究一下它的源代码。
Spring提供自动检测“刻板”类的function。 要自动检测这些类并注册相应的bean,需要包含[context:component-scan]元素。
我不确定它是否会帮助你,但你可以看看Apache Commons-Discovery项目。
发现项目
使用Spring,您也可以使用AnnotationUtils类来编写以下内容。 即:
Class<?> clazz = AnnotationUtils.findAnnotationDeclaringClass(Target.class, null);
有关更多详细信息和所有不同的方法,请查看官方文档: https : //docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AnnotationUtils.html
Java没有“发现”。 我知道的唯一方法是扫描.class文件应该在的目录,parsing名称并使用它。 可怕的是丑陋的,也许现在有一个更好的包裹 – 我没有看几年。
通常这个问题通过包含一个属性文件或一个带有类名的.xml文件来解决。
我也有兴趣听到更好的答案。
类加载器API没有“枚举”方法,因为类加载是一个“按需”活动 – 通常在你的类path中有成千上万个类,只有一小部分是需要的(rt.jar现在只有48MB!)。
所以,即使你能枚举所有的类,这将是非常耗时间和内存的。
简单的方法是在安装文件中列出相关的类(xml或任何适合您的幻想)。 如果您想自动执行此操作,请将自己限制为一个JAR或一个类目录。
Google Reflections似乎比Spring快得多。 发现这个function请求,以区别这个区别: http : //www.opensaga.org/jira/browse/OS-738
这是使用Reflections的原因,因为我的应用程序的启动时间在开发过程中非常重要。 思考似乎也很容易使用我的用例(find一个接口的所有实现者)。
如果你想要一个Scala库,使用Sclasner: https : //github.com/ngocdaothanh/sclasner