1. Overview
1.概述
In this brief tutorial, we’ll talk about the Classgraph library — what it helps with and how we can use it.
在这个简短的教程中,我们将谈论Classgraph库–它有什么帮助以及我们如何使用它。
Classgraph helps us to find target resources in the Java classpath, builds metadata about the resources found and provides convenient APIs for working with the metadata.
Classgraph帮助我们在Java classpath中找到目标资源,建立关于所找到的资源的元数据,并提供方便的API来处理元数据。
This use-case is very popular in Spring-based applications, where components marked with stereotype annotations are automatically registered in the application context. However, we can exploit that approach for custom tasks as well. For example, we might want to find all classes with a particular annotation, or all resource files with a certain name.
这种用例在基于Spring的应用程序中非常流行,在这种情况下,用定型注解标记的组件会自动注册在应用程序上下文中。然而,我们也可以利用这种方法来完成自定义任务。例如,我们可能想找到所有带有特定注解的类,或者所有带有特定名称的资源文件。
The cool thing is that Classgraph is fast, as it works on the byte-code level, meaning the inspected classes are not loaded to the JVM, and it doesn’t use reflection for processing.
最酷的是,Classgraph非常快,因为它在字节码层面上工作,这意味着被检查的类不会被加载到JVM中,而且它不使用反射来处理。
2. Maven Dependencies
2.Maven的依赖性
First, let’s add the classgraph library to our pom.xml:
首先,让我们把classgraph库添加到我们的pom.xml。
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>4.8.28</version>
</dependency>
In the next sections, we’ll look into several practical examples with the library’s API.
在接下来的章节中,我们将研究几个使用该库的API的实际例子。
3. Basic Usage
3.基本的使用方法
There are three basic steps to using the library:
使用图书馆有三个基本步骤:。
- Set up scan options – for example, target package(s)
- Perform the scan
- Work with the scan results
Let’s create the following domain for our example setup:
让我们为我们的示例设置创建以下域。
@Target({TYPE, METHOD, FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String value() default "";
}
@TestAnnotation
public class ClassWithAnnotation {
}
Now let’s see the 3 steps above on an example of looking for classes with the @TestAnnotation:
现在让我们在寻找带有@TestAnnotation的类的例子上看看上面的3个步骤。
try (ScanResult result = new ClassGraph().enableClassInfo().enableAnnotationInfo()
.whitelistPackages(getClass().getPackage().getName()).scan()) {
ClassInfoList classInfos = result.getClassesWithAnnotation(TestAnnotation.class.getName());
assertThat(classInfos).extracting(ClassInfo::getName).contains(ClassWithAnnotation.class.getName());
}
Let’s break down the example above:
我们来分析一下上面的例子。
- we started by setting up the scan options (we’ve configured the scanner to parse only class and annotation info, as well instructing it to parse only files from the target package)
- we performed the scan using the ClassGraph.scan() method
- we used the ScanResult to find annotated classes by calling the getClassWithAnnotation() method
As we’ll also see in the next examples, the ScanResult object can contain a lot of information about the APIs we want to inspect, such as the ClassInfoList.
在接下来的例子中,我们还将看到,ScanResult对象可以包含很多我们要检查的API的信息,比如ClassInfoList.。
4. Filtering by Method Annotation
4.通过方法注解进行过滤
Let’s expand our example to method annotations:
让我们把我们的例子扩展到方法注释。
public class MethodWithAnnotation {
@TestAnnotation
public void service() {
}
}
We can find all classes that have methods marked by the target annotation using a similar method — getClassesWithMethodAnnotations():
我们可以使用一个类似的方法–getClassesWithMethodAnnotations()找到所有拥有目标注解标记的方法的类:。
try (ScanResult result = new ClassGraph().enableAllInfo()
.whitelistPackages(getClass().getPackage().getName()).scan()) {
ClassInfoList classInfos = result.getClassesWithMethodAnnotation(TestAnnotation.class.getName());
assertThat(classInfos).extracting(ClassInfo::getName).contains(MethodWithAnnotation.class.getName());
}
The method returns a ClassInfoList object containing information about the classes that match the scan.
该方法返回一个ClassInfoList对象,其中包含与扫描相匹配的类的信息。
5. Filtering by Annotation Parameter
5.按注释参数过滤
Let’s also see how we can find all classes with methods marked by the target annotation and with a target annotation parameter value.
我们也来看看如何找到所有具有目标注解标记的方法和具有目标注解参数值的类。
First, let’s define classes containing methods with the @TestAnnotation, with 2 different parameter values:
首先,让我们定义包含有@TestAnnotation,的方法的类,有两个不同的参数值。
public class MethodWithAnnotationParameterDao {
@TestAnnotation("dao")
public void service() {
}
}
public class MethodWithAnnotationParameterWeb {
@TestAnnotation("web")
public void service() {
}
}
Now, let’s iterate through the ClassInfoList result, and verify each method’s annotations:
现在,让我们遍历ClassInfoList结果,并验证每个方法的注释。
try (ScanResult result = new ClassGraph().enableAllInfo()
.whitelistPackages(getClass().getPackage().getName()).scan()) {
ClassInfoList classInfos = result.getClassesWithMethodAnnotation(TestAnnotation.class.getName());
ClassInfoList webClassInfos = classInfos.filter(classInfo -> {
return classInfo.getMethodInfo().stream().anyMatch(methodInfo -> {
AnnotationInfo annotationInfo = methodInfo.getAnnotationInfo(TestAnnotation.class.getName());
if (annotationInfo == null) {
return false;
}
return "web".equals(annotationInfo.getParameterValues().getValue("value"));
});
});
assertThat(webClassInfos).extracting(ClassInfo::getName)
.contains(MethodWithAnnotationParameterWeb.class.getName());
}
Here, we’ve used the AnnotationInfo and MethodInfo metadata classes to find metadata on the methods and annotations we want to check.
在这里,我们使用了AnnotationInfo和MethodInfo元数据类来寻找我们想要检查的方法和注解的元数据。。
6. Filtering by Field Annotation
6.按字段注解过滤
We can also use the getClassesWithFieldAnnotation() method to filter a ClassInfoList result based on field annotations:
我们还可以使用getClassesWithFieldAnnotation()方法来根据字段注释过滤ClassInfoList结果。
public class FieldWithAnnotation {
@TestAnnotation
private String s;
}
try (ScanResult result = new ClassGraph().enableAllInfo()
.whitelistPackages(getClass().getPackage().getName()).scan()) {
ClassInfoList classInfos = result.getClassesWithFieldAnnotation(TestAnnotation.class.getName());
assertThat(classInfos).extracting(ClassInfo::getName).contains(FieldWithAnnotation.class.getName());
}
7. Finding Resources
7.寻找资源
Finally, we’ll have a look at how we can find information on classpath resources.
最后,我们将看一下如何找到classpath资源的信息。
Let’s create a resource file in the classgraph classpath root directory — for example, src/test/resources/classgraph/my.config — and give it some content:
让我们在classgraph classpath根目录下创建一个资源文件–例如,src/test/resources/classgraph/my.config–并给它一些内容。
my data
We can now find the resource and get its contents:
我们现在可以找到该资源并获得其内容。
try (ScanResult result = new ClassGraph().whitelistPaths("classgraph").scan()) {
ResourceList resources = result.getResourcesWithExtension("config");
assertThat(resources).extracting(Resource::getPath).containsOnly("classgraph/my.config");
assertThat(resources.get(0).getContentAsString()).isEqualTo("my data");
}
We can see here we’ve used the ScanResult’s getResourcesWithExtension() method to look for our specific file. The class has a few other useful resource-related methods, such as getAllResources(), getResourcesWithPath() and getResourcesMatchingPattern().
我们在这里可以看到,我们使用了ScanResult 的getResourcesWithExtension()方法来寻找我们的特定文件。该类有一些其他有用的资源相关方法,例如getAllResources()、getResourcesWithPath()和getResourcesMatchingPattern()。
These methods return a ResourceList object, which can be further used to iterate through and manipulate Resource objects.
这些方法返回一个ResourceList对象,该对象可被进一步用于遍历和操作Resource对象。
8. Instantiation
8.实例化
When we want to instantiate found classes, it’s very important to do that not via Class.forName, but by using the library method ClassInfo.loadClass.
当我们想把找到的类实例化时,非常重要的是不要通过Class.forName,,而是使用库中的ClassInfo.loadClass方法来实现。
The reason is that Classgraph uses its own class loader to load classes from some JAR files. So, if we use Class.forName, the same class might be loaded more than once by different class loaders, and this might lead to non-trivial bugs.
原因是Classgraph使用它自己的类加载器来从一些JAR文件中加载类。因此,如果我们使用Class.forName,同一个类可能会被不同的类加载器加载不止一次,这可能会导致非实质性的错误。
9. Conclusion
9.结论
In this article, we learned how to effectively find classpath resources and inspect their contents with the Classgraph library.
在这篇文章中,我们学习了如何有效地找到classpath资源并使用Classgraph库检查其内容。
As usual, the complete source code for this article is available over on GitHub.
像往常一样,本文的完整源代码可在GitHub上获得,。