在运行时扫描 Java 注释

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/259140/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 12:08:09  来源:igfitidea点击:

Scanning Java annotations at runtime

javaannotationsclassloader

提问by Alotor

What is the best way of searching the whole classpath for an annotated class?

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

I'm doing a library and I want to allow the users to annotate their classes, so when the Web application starts I need to scan the whole classpath for certain annotation.

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

Do you know a library or a Java facility to do this?

你知道一个库或一个 Java 工具来做到这一点吗?

Edit: I'm thinking about something like the new functionality for Java EE 5 Web Services or EJB's. You annotate your class with @WebServiceor @EJBand the system finds these classes while loading so they are accessible remotely.

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

采纳答案by Arthur Ronald

Use org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider

使用org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider

API

应用程序接口

A component provider that scans the classpath from a base package. It then applies exclude and include filters to the resulting classes to find candidates.

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

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

回答by mfx

The Classloader API doesn't have an "enumerate" method, because class loading is an "on-demand" activity -- you usually have thousands of classes in your classpath, only a fraction of which will ever be needed (the rt.jar alone is 48MB nowadays!).

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

So, even if you couldenumerate all classes, this would be very time- and memory-consuming.

因此,即使您可以枚举所有类,这也将非常耗时和消耗内存。

The simple approach is to list the concerned classes in a setup file (xml or whatever suits your fancy); if you want to do this automatically, restrict yourself to one JAR or one class directory.

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

回答by Jonathan

And another solution is Google reflections.

另一个解决方案是Google 反射

Quick review:

快速复审:

  • Spring solution is the way to go if you're using Spring. Otherwise it's a big dependency.
  • Using ASM directly is a bit cumbersome.
  • Using Java Assist directly is clunky too.
  • Annovention is super lightweight and convenient. No maven integration yet.
  • Google reflections pulls in Google collections. Indexes everything and then is super fast.
  • 如果您使用的是 Spring,则 Spring 解决方案是可行的方法。否则它是一个很大的依赖。
  • 直接使用ASM 有点麻烦。
  • 直接使用 Java Assist 也很笨拙。
  • Annovention 超轻量级且方便。还没有 Maven 集成。
  • 谷歌反射拉入谷歌收藏。索引一切,然后超级快。

回答by rmuller

If you want a really light weight(no dependencies, simple API, 15 kb jar file) and very fastsolution, take a look at annotation-detectorfound at https://github.com/rmuller/infomas-asl

如果你想要一个真正重量轻(无依赖性,简单的API,15 kb的jar文件)和非常快的解决方案,来看看annotation-detector在发现https://github.com/rmuller/infomas-asl

Disclaimer: I am the author.

免责声明:我是作者。

回答by S?awek

You can use Java Pluggable Annotation Processing API to write annotation processor which will be executed during the compilation process and will collect all annotated classes and build the index file for runtime use.

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

This is the fastest way possible to do annotated class discovery because you don't need to scan your classpath at runtime, which is usually very slow operation. Also this approach works with any classloader and not only with URLClassLoaders usually supported by runtime scanners.

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

The above mechanism is already implemented in ClassIndexlibrary.

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

To use it annotate your custom annotation with @IndexAnnotatedmeta-annotation. This will create at compile time an index file: META-INF/annotations/com/test/YourCustomAnnotation listing all annotated classes. You can acccess the index at runtime by executing:

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

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

回答by Martin Aubele

Google Reflectionsseems to be much faster than Spring. Found this feature request that adresses this difference: http://www.opensaga.org/jira/browse/OS-738

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

This is a reason to use Reflections as startup time of my application is really important during development. Reflections seems also to be very easy to use for my use case (find all implementers of an interface).

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

回答by Luke Hutchison

You can find classes with any given annotation with ClassGraph, as well as searching for other criteria of interest, e.g. classes that implement a given interface. (Disclaimer, I am the author of ClassGraph.) ClassGraph can build an abstract representation of the entire class graph (all classes, annotations, methods, method parameters, and fields) in memory, for all classes on the classpath, or for classes in whitelisted packages, and you can query that class graph however you want. ClassGraph supports more classpath specification mechanisms and classloadersthan any other scanner, and also works seamlessly with the new JPMS module system, so if you base your code on ClassGraph, your code will be maximally portable. See the API here.

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

回答by magiccrafter

With Spring you can also just write the following using AnnotationUtils class. i.e.:

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

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

For more details and all different methods check official docs: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AnnotationUtils.html

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

回答by kenobiwan

Is it too late to answer. I would say, its better to go by Libraries like ClassPathScanningCandidateComponentProvideror like Scannotations

是不是来不及回答了。我会说,最好使用ClassPathScanningCandidateComponentProviderScannotations 之类的

But even after somebody wants to try some hands on it with classLoader, I have written some on my own to print the annotations from classes in a package:

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

}
}

And in config File, you put the package name and unmarshall it to a class .

在 config File 中,您将包名放入并将其解组为 class 。

回答by dzikoysk

If you're looking for an alternative to reflectionsI'd like to recommend Panda Utilities - AnnotationsScanner. It's a Guava-free (Guava has ~3MB, Panda Utilities has ~200kb) scanner based on the reflections library source code.

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

It's also dedicated for future-based searches. If you'd like to scan multiple times included sources or even provide an API, which allows someone scanning current classpath, AnnotationsScannerProcesscaches all fetched ClassFiles, so it's really fast.

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

Simple example of AnnotationsScannerusage:

简单的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);