在运行时扫描Java注释

时间:2008-11-03 16:17:18

标签: java annotations classloader

为带注释的类搜索整个类路径的最佳方法是什么?

我正在做一个库,我想允许用户注释他们的类,所以当Web应用程序启动时,我需要扫描整个类路径以获取某些注释。

您是否知道要执行此操作的库或Java工具?

编辑:我正在考虑类似Java EE 5 Web服务或EJB的新功能。您使用@WebService@EJB为您的班级添加注释,系统会在加载时找到这些类,以便远程访问它们。

18 个答案:

答案 0 :(得分:195)

使用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());

答案 1 :(得分:140)

另一个解决方案是Google reflections

快速审核:

  • 如果您使用Spring,Spring解决方案就是您的选择。否则这是一个很大的依赖。
  • 直接使用ASM有点麻烦。
  • 直接使用Java Assist也很笨拙。
  • Annovention超轻便,方便。还没有maven整合。
  • 谷歌的反思引入了Google收藏。索引一切然后超快。

答案 2 :(得分:40)

您可以使用ClassGraph找到包含任何给定注释的类,以及搜索其他感兴趣的标准,例如:实现给定接口的类。 (免责声明,我是ClassGraph的作者。)ClassGraph可以在内存中为类路径上的所有类或类中的类构建整个类图(所有类,注释,方法,方法参数和字段)的抽象表示。列入白名单的软件包,您可以根据需要查询该类图。 ClassGraph比任何其他扫描程序都支持more classpath specification mechanisms and classloaders,并且还可以与新的JPMS模块系统无缝协作,因此如果您将代码基于ClassGraph,您的代码将最大程度地可移植。 See the API here.

答案 3 :(得分:22)

如果您想要一个非常轻量级(没有依赖项,简单API,15 kb jar文件)和非常快解决方案,请查看annotation-detector发现于https://github.com/rmuller/infomas-asl

免责声明:我是作者。

答案 4 :(得分:17)

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

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

上述机制已在ClassIndex库中实现。

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

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

答案 5 :(得分:6)

尝试Scannotation

它可用于搜索类路径或Web应用程序li​​b目录以获取特定注释。

答案 6 :(得分:5)

答案 7 :(得分:4)

使用Spring,您也可以使用AnnotationUtils类编写以下内容。即:

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

有关详细信息和所有不同方法,请查看官方文档: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AnnotationUtils.html

答案 8 :(得分:3)

稍微偏离主题,但是Spring也会使用<context:component-scan>做类似的事情,你可以研究它的源代码吗?

  

Spring提供了自动检测'刻板'类[...]的能力。要自动检测这些类并注册相应的bean,需要包含[context:component-scan element]。

答案 9 :(得分:3)

回答为时已晚。 我想说,最好去像ClassPathScanningCandidateComponentProvider或类似Scannotations

这样的图书馆

但即使有人想用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;

}
}

在配置文件中,您输入包名称并将其解组到类中。

答案 10 :(得分:2)

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

因此,即使你能够枚举所有类,这也会耗费时间和内存。

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

答案 11 :(得分:2)

我不确定它是否会对您有所帮助,但您可以查看apache commons-discovery项目。

discovery project

答案 12 :(得分:1)

Google Reflections似乎比Spring快得多。找到了解决此差异的此功能请求:http://www.opensaga.org/jira/browse/OS-738

这是使用Reflections的一个原因,因为我的应用程序的启动时间在开发过程中非常重要。反射似乎也很容易用于我的用例(找到界面的所有实现者)。

答案 13 :(得分:1)

如果您正在寻找reflections的替代产品,我想推荐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);

答案 14 :(得分:1)

Google Reflection(如果您也想发现界面)。

Spring ClassPathScanningCandidateComponentProvider没有发现接口。

答案 15 :(得分:1)

zapp有一个很棒的评论,涵盖了所有这些答案:

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

答案 16 :(得分:0)

如果您需要Scala库,请使用Sclasner: https://github.com/ngocdaothanh/sclasner

答案 17 :(得分:0)

Spring有一个叫做AnnotatedTypeScanner的类。
此类内部使用

ClassPathScanningCandidateComponentProvider

此类具有用于实际扫描 classpath 资源的代码。它通过使用运行时可用的类元数据来实现此目的。

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

/**
     * 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;
    }