在运行时扫描Java注释

在运行时扫描Java注释,java,annotations,classloader,Java,Annotations,Classloader,搜索带注释类的整个类路径的最佳方法是什么 我正在做一个库,我想允许用户对他们的类进行注释,所以当Web应用程序启动时,我需要扫描整个类路径以获得某些注释 你知道一个库或Java工具可以做到这一点吗 编辑:我正在考虑JavaEE5Web服务或EJB的新功能。您使用@WebService或@EJB注释类,系统在加载时会找到这些类,以便远程访问它们。类加载器API没有“枚举”方法,因为类加载是一种“按需”活动——您的类路径中通常有数千个类,只需要其中的一小部分(现在仅rt.jar就有48MB!) 因此

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

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

你知道一个库或Java工具可以做到这一点吗


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

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

因此,即使您可以枚举所有类,这也会非常耗时和占用内存

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

使用

原料药

从基本包扫描类路径的组件提供程序。然后将排除和包含筛选器应用于结果类以查找候选类

ClassPathScanningCandidateComponentProvider扫描仪=
新建ClassPathScanningCandidateComponentProvider();
addIncludeFilter(新的AnnotationTypeFilter(.class));
对于(BeanDefinition bd:scanner.findCandidateComponents())
System.out.println(bd.getBeanClassName());
另一个解决方案是

快速回顾:

  • 如果您正在使用Spring,Spring解决方案就是一个不错的选择。否则,这是一个很大的依赖
  • 直接使用ASM有点麻烦
  • 直接使用Java助手也很笨拙
  • Annovenation超轻且方便。还没有maven集成
  • 谷歌反射带来了谷歌收藏。索引所有内容,然后是超快速的

如果您想要一个真正的轻量级(无依赖项,简单API,15 kb jar文件)和非常快速的解决方案,请查看
注释检测器


免责声明:我是作者。

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

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

上述机制已在库中实现

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

ClassIndex.getAnnotated(com.test.YourCustomAnnotation.class)
谷歌似乎比春天快得多。找到解决此差异的此功能请求:


这就是使用反射的原因,因为我的应用程序的启动时间在开发过程中非常重要。对于我的用例来说,反射似乎也很容易使用(查找接口的所有实现者)。

您可以查找具有任何给定注释的类,以及搜索其他感兴趣的条件,例如实现给定接口的类。(免责声明,我是ClassGraph的作者。)ClassGraph可以为内存中的整个类图(所有类、注释、方法、方法参数和字段)、类路径上的所有类或白名单包中的类构建抽象表示,并且您可以根据需要查询该类图。ClassGraph比任何其他扫描器都支持,并且可以无缝地与新的JPMS模块系统配合使用,因此如果您的代码基于ClassGraph,那么您的代码将具有最大的可移植性

使用Spring,您还可以使用AnnotationUtils类编写以下内容。i、 e:

Class<?> clazz = AnnotationUtils.findAnnotationDeclaringClass(Target.class, null);
Class clazz=AnnotationUtils.findAnnotationDeclaringClass(Target.Class,null);
有关更多详细信息和所有不同方法,请查看官方文档: 现在回答是否太晚了。 我想说的是,它更好地去图书馆喜欢或喜欢

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

}
}
公共类元素扫描器{
公共元素(){
试一试{
//从配置文件中获取包名
字符串packageName=readConfig();
//加载加载此类的类加载器。
ClassLoader ClassLoader=getClass().getClassLoader();
//将包结构更改为目录结构
字符串packagePath=packageName.replace('.','/');
URL URL=classLoader.getResource(packagePath);
//获取指定URL路径中的所有类文件。
文件夹=新文件(url.getPath());
File[]classes=folder.listFiles();
int size=classes.length;
列表>();

对于(int i=0;i,如果您正在寻找我想推荐的替代品。这是一款基于反射库源代码的无番石榴(番石榴有~3MB,Panda Utilities有~200kb)扫描仪

它还专门用于未来的搜索。如果您想多次扫描包含的源代码,甚至提供一个允许某人扫描当前类路径的API,
annotationsCannerProcess
缓存所有获取的
类文件
,因此它非常快

annotationsCanner
用法的简单示例:

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

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

Set<Class<?>> classes = process.createSelector()
        .selectTypesAnnotatedWith(AnnotationTest.class);
AnnotationsScanner scanner=AnnotationsScanner.createScanner()
.includeSources(例如application.class)
.build();
AnnotationsScanner scanner = AnnotationsScanner.createScanner()
        .includeSources(ExampleApplication.class)
        .build();

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

Set<Class<?>> classes = process.createSelector()
        .selectTypesAnnotatedWith(AnnotationTest.class);
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)