0
# Querying and Results
1
2
The ScanResult class provides comprehensive querying capabilities for finding classes, interfaces, annotations, and resources from the scanned classpath. It implements Closeable and must be properly closed after use.
3
4
## Basic Result Access
5
6
```java { .api }
7
import io.github.classgraph.ScanResult;
8
import io.github.classgraph.ClassInfo;
9
import io.github.classgraph.ClassInfoList;
10
import io.github.classgraph.ResourceList;
11
import io.github.classgraph.MethodInfoList;
12
import io.github.classgraph.AnnotationInfo;
13
import java.util.Map;
14
import java.util.List;
15
import java.util.AbstractList;
16
import java.util.ArrayList;
17
import java.util.Arrays;
18
import java.util.Properties;
19
import java.io.InputStream;
20
import java.nio.charset.StandardCharsets;
21
import java.util.regex.Pattern;
22
```
23
24
### Class Information Queries
25
26
```java { .api }
27
try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan()) {
28
// Get all classes found during scan
29
ClassInfoList allClasses = scanResult.getAllClasses();
30
31
// Get specific class by name
32
ClassInfo classInfo = scanResult.getClassInfo("com.example.MyClass");
33
34
// Get classes by type
35
ClassInfoList standardClasses = scanResult.getAllStandardClasses(); // Non-interface classes
36
ClassInfoList interfaces = scanResult.getAllInterfaces();
37
ClassInfoList annotations = scanResult.getAllAnnotations();
38
ClassInfoList enums = scanResult.getAllEnums();
39
ClassInfoList records = scanResult.getAllRecords(); // JDK 14+
40
41
// Combined queries
42
ClassInfoList interfacesAndAnnotations = scanResult.getAllInterfacesAndAnnotations();
43
44
// Get as Map for O(1) lookup
45
Map<String, ClassInfo> classMap = scanResult.getAllClassesAsMap();
46
}
47
```
48
49
## Inheritance and Implementation Queries
50
51
### Subclass and Superclass Queries
52
53
```java { .api }
54
try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan()) {
55
// Find all subclasses (direct and indirect)
56
ClassInfoList subclasses = scanResult.getSubclasses("java.util.AbstractList");
57
ClassInfoList subclassesByClass = scanResult.getSubclasses(AbstractList.class);
58
59
// Find superclass hierarchy
60
ClassInfoList superclasses = scanResult.getSuperclasses("java.util.ArrayList");
61
ClassInfoList superclassesByClass = scanResult.getSuperclasses(ArrayList.class);
62
63
// Find interfaces of a class
64
ClassInfoList interfaces = scanResult.getInterfaces("java.util.ArrayList");
65
ClassInfoList interfacesByClass = scanResult.getInterfaces(ArrayList.class);
66
67
// Find all implementers of an interface
68
ClassInfoList implementers = scanResult.getClassesImplementing("java.util.List");
69
ClassInfoList implementersByClass = scanResult.getClassesImplementing(List.class);
70
}
71
```
72
73
### Complex Inheritance Examples
74
75
```java { .api }
76
// Find all service implementations
77
ClassInfoList serviceImpls = scanResult.getClassesImplementing("com.example.Service");
78
79
// Find all classes extending a specific abstract class
80
ClassInfoList controllerSubclasses = scanResult.getSubclasses("com.example.AbstractController");
81
82
// Find all classes that implement multiple interfaces
83
ClassInfoList multiInterface = scanResult.getAllClasses()
84
.filter(classInfo ->
85
classInfo.implementsInterface("java.io.Serializable") &&
86
classInfo.implementsInterface("java.lang.Cloneable"));
87
```
88
89
### Set Operations on ClassInfoList
90
91
ClassInfoList supports set operations for combining and filtering results:
92
93
```java { .api }
94
// Get two different sets of classes
95
ClassInfoList serviceClasses = scanResult.getClassesImplementing("com.example.Service");
96
ClassInfoList annotatedClasses = scanResult.getClassesWithAnnotation("com.example.Component");
97
98
// Union - classes that are in either list
99
ClassInfoList unionClasses = serviceClasses.union(annotatedClasses);
100
101
// Intersection - classes that are in both lists
102
ClassInfoList intersectionClasses = serviceClasses.intersect(annotatedClasses);
103
104
// Exclusion - classes in first list but not in second
105
ClassInfoList excludedClasses = serviceClasses.exclude(annotatedClasses);
106
107
// Multiple set operations
108
ClassInfoList controllers = scanResult.getClassesWithAnnotation("org.springframework.stereotype.Controller");
109
ClassInfoList restControllers = scanResult.getClassesWithAnnotation("org.springframework.web.bind.annotation.RestController");
110
ClassInfoList services = scanResult.getClassesWithAnnotation("org.springframework.stereotype.Service");
111
112
// All Spring components
113
ClassInfoList allSpringComponents = controllers.union(restControllers, services);
114
```
115
116
## Annotation Queries
117
118
### Basic Annotation Queries
119
120
```java { .api }
121
try (ScanResult scanResult = new ClassGraph().enableAnnotationInfo().scan()) {
122
// Classes with specific annotation
123
ClassInfoList annotatedClasses = scanResult.getClassesWithAnnotation("javax.persistence.Entity");
124
ClassInfoList annotatedByClass = scanResult.getClassesWithAnnotation(Entity.class);
125
126
// Classes with ALL specified annotations
127
ClassInfoList withAllAnnotations = scanResult.getClassesWithAllAnnotations(
128
"javax.persistence.Entity", "javax.persistence.Table");
129
130
// Classes with ANY of the specified annotations
131
ClassInfoList withAnyAnnotations = scanResult.getClassesWithAnyAnnotation(
132
"javax.ws.rs.GET", "javax.ws.rs.POST", "javax.ws.rs.PUT");
133
134
// Get annotations on a specific class
135
ClassInfoList annotations = scanResult.getAnnotationsOnClass("com.example.MyClass");
136
}
137
```
138
139
### Method and Field Annotation Queries
140
141
```java { .api }
142
// Find classes that have methods with specific annotations
143
ClassInfoList classesWithMethodAnnotation = scanResult.getClassesWithMethodAnnotation("javax.ws.rs.GET");
144
145
// Find classes that have fields with specific annotations
146
ClassInfoList classesWithFieldAnnotation = scanResult.getClassesWithFieldAnnotation("javax.persistence.Column");
147
148
// Find classes with method parameter annotations
149
ClassInfoList classesWithParamAnnotation = scanResult.getClassesWithMethodParameterAnnotation("javax.validation.Valid");
150
```
151
152
### Advanced Annotation Processing
153
154
```java { .api }
155
// Process REST endpoints
156
ClassInfoList restControllers = scanResult.getClassesWithAnnotation("org.springframework.web.bind.annotation.RestController");
157
158
for (ClassInfo controllerClass : restControllers) {
159
System.out.println("Controller: " + controllerClass.getName());
160
161
// Find all request mapping methods
162
MethodInfoList requestMethods = controllerClass.getDeclaredMethodInfo()
163
.filter(methodInfo ->
164
methodInfo.hasAnnotation("org.springframework.web.bind.annotation.RequestMapping") ||
165
methodInfo.hasAnnotation("org.springframework.web.bind.annotation.GetMapping") ||
166
methodInfo.hasAnnotation("org.springframework.web.bind.annotation.PostMapping"));
167
168
for (MethodInfo method : requestMethods) {
169
System.out.println(" Endpoint: " + method.getName());
170
171
// Extract annotation values
172
AnnotationInfo mapping = method.getAnnotationInfo("org.springframework.web.bind.annotation.GetMapping");
173
if (mapping != null) {
174
String[] paths = (String[]) mapping.getParameterValues().getValue("value");
175
System.out.println(" Paths: " + Arrays.toString(paths));
176
}
177
}
178
}
179
```
180
181
## Resource Queries
182
183
### Basic Resource Access
184
185
```java { .api }
186
try (ScanResult scanResult = new ClassGraph().acceptPaths("META-INF", "config").scan()) {
187
// Get all resources found
188
ResourceList allResources = scanResult.getAllResources();
189
Map<String, ResourceList> resourceMap = scanResult.getAllResourcesAsMap();
190
191
// Find resources by specific path
192
ResourceList configFiles = scanResult.getResourcesWithPath("META-INF/spring.factories");
193
194
// Find resources ignoring accept/reject filters
195
ResourceList allSpringFiles = scanResult.getResourcesWithPathIgnoringAccept("META-INF/spring.factories");
196
197
// Find by filename (leaf name)
198
ResourceList propertiesFiles = scanResult.getResourcesWithLeafName("application.properties");
199
200
// Find by extension
201
ResourceList jsonFiles = scanResult.getResourcesWithExtension("json");
202
ResourceList xmlFiles = scanResult.getResourcesWithExtension("xml");
203
}
204
```
205
206
### Pattern-Based Resource Queries
207
208
```java { .api }
209
import java.util.regex.Pattern;
210
211
// Find resources matching regex pattern
212
Pattern logbackPattern = Pattern.compile(".*logback.*\\.xml");
213
ResourceList logbackConfigs = scanResult.getResourcesMatchingPattern(logbackPattern);
214
215
// Find resources matching wildcard pattern
216
ResourceList sqlFiles = scanResult.getResourcesMatchingWildcard("**/*.sql");
217
ResourceList configFiles = scanResult.getResourcesMatchingWildcard("**/config/**");
218
```
219
220
### Resource Content Processing
221
222
```java { .api }
223
// Process JSON configuration files
224
scanResult.getResourcesWithExtension("json")
225
.forEachByteArrayIgnoringIOException((resource, content) -> {
226
String json = new String(content, StandardCharsets.UTF_8);
227
System.out.println("Processing " + resource.getPath() + ": " + json.length() + " bytes");
228
// Parse JSON and process...
229
});
230
231
// Load properties files
232
scanResult.getResourcesWithExtension("properties")
233
.forEach(resource -> {
234
try (InputStream inputStream = resource.open()) {
235
Properties props = new Properties();
236
props.load(inputStream);
237
System.out.println("Loaded " + props.size() + " properties from " + resource.getPath());
238
} catch (IOException e) {
239
System.err.println("Failed to load " + resource.getPath() + ": " + e.getMessage());
240
}
241
});
242
```
243
244
## Package and Module Information
245
246
### Package Queries
247
248
```java { .api }
249
try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan()) {
250
// Get all packages
251
PackageInfoList allPackages = scanResult.getPackageInfo();
252
253
// Get specific package
254
PackageInfo packageInfo = scanResult.getPackageInfo("com.example");
255
256
if (packageInfo != null) {
257
// Get classes in package
258
ClassInfoList classesInPackage = packageInfo.getClassInfo();
259
260
// Get package annotations
261
AnnotationInfoList packageAnnotations = packageInfo.getAnnotationInfo();
262
263
// Package hierarchy
264
PackageInfo parent = packageInfo.getParent();
265
PackageInfoList children = packageInfo.getChildren();
266
}
267
}
268
```
269
270
### Module Queries (JPMS)
271
272
```java { .api }
273
// Get all modules
274
ModuleInfoList allModules = scanResult.getModuleInfo();
275
276
// Get specific module
277
ModuleInfo moduleInfo = scanResult.getModuleInfo("java.base");
278
279
if (moduleInfo != null) {
280
// Get classes in module
281
ClassInfoList classesInModule = moduleInfo.getClassInfo();
282
283
// Get packages in module
284
PackageInfoList packagesInModule = moduleInfo.getPackageInfo();
285
286
// Module location and reference
287
URI location = moduleInfo.getLocation();
288
ModuleRef moduleRef = moduleInfo.getModuleRef();
289
}
290
```
291
292
## Dependency Analysis
293
294
When `enableInterClassDependencies()` is enabled:
295
296
```java { .api }
297
try (ScanResult scanResult = new ClassGraph()
298
.enableInterClassDependencies()
299
.acceptPackages("com.example")
300
.scan()) {
301
302
// Get class dependency map (what each class depends on)
303
Map<ClassInfo, ClassInfoList> dependencyMap = scanResult.getClassDependencyMap();
304
305
// Get reverse dependency map (what depends on each class)
306
Map<ClassInfo, ClassInfoList> reverseDependencyMap = scanResult.getReverseClassDependencyMap();
307
308
// Analyze specific class dependencies
309
ClassInfo serviceClass = scanResult.getClassInfo("com.example.UserService");
310
if (serviceClass != null) {
311
ClassInfoList dependencies = serviceClass.getClassDependencies();
312
System.out.println("UserService depends on " + dependencies.size() + " other classes");
313
314
// Find what depends on UserService
315
ClassInfoList dependents = reverseDependencyMap.get(serviceClass);
316
if (dependents != null) {
317
System.out.println(dependents.size() + " classes depend on UserService");
318
}
319
}
320
}
321
```
322
323
## Change Detection and Monitoring
324
325
```java { .api }
326
try (ScanResult scanResult = new ClassGraph().scan()) {
327
// Check if classpath has been modified since scan
328
boolean modified = scanResult.classpathContentsModifiedSinceScan();
329
330
if (modified) {
331
System.out.println("Classpath has changed - consider rescanning");
332
333
// Get timestamp of latest modification
334
long lastModified = scanResult.classpathContentsLastModifiedTime();
335
System.out.println("Last modified: " + new Date(lastModified));
336
}
337
}
338
```
339
340
## Class Loading from Results
341
342
### Safe Class Loading
343
344
```java { .api }
345
// Load class by name with error handling
346
try {
347
Class<?> clazz = scanResult.loadClass("com.example.MyClass", false); // false = don't initialize
348
System.out.println("Loaded class: " + clazz.getName());
349
} catch (Exception e) {
350
System.err.println("Failed to load class: " + e.getMessage());
351
}
352
353
// Load and cast class with type safety
354
try {
355
Class<? extends Service> serviceClass = scanResult.loadClass("com.example.MyService", Service.class, false);
356
Service instance = serviceClass.getDeclaredConstructor().newInstance();
357
} catch (Exception e) {
358
System.err.println("Failed to instantiate service: " + e.getMessage());
359
}
360
```
361
362
### Bulk Class Loading
363
364
```java { .api }
365
ClassInfoList serviceClasses = scanResult.getClassesImplementing("com.example.Service");
366
367
// Load all service classes safely
368
List<Class<? extends Service>> loadedClasses = serviceClasses.loadClasses(Service.class, false);
369
370
// Filter out classes that failed to load
371
List<Class<? extends Service>> validClasses = loadedClasses.stream()
372
.filter(Objects::nonNull)
373
.collect(toList());
374
375
System.out.println("Successfully loaded " + validClasses.size() + " service classes");
376
```
377
378
## Serialization and Persistence
379
380
```java { .api }
381
try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan()) {
382
// Serialize scan results to JSON
383
String json = scanResult.toJSON();
384
385
// Serialize with formatting
386
String prettyJson = scanResult.toJSON(2); // 2-space indentation
387
388
// Save to file for caching
389
Files.write(Paths.get("scan-results.json"), json.getBytes());
390
}
391
392
// Later, deserialize from JSON
393
String json = Files.readString(Paths.get("scan-results.json"));
394
try (ScanResult cachedResult = ScanResult.fromJSON(json)) {
395
// Use cached results (classes can't be loaded from deserialized results)
396
boolean fromCache = cachedResult.isObtainedFromDeserialization();
397
System.out.println("Using cached scan results: " + fromCache);
398
399
ClassInfoList allClasses = cachedResult.getAllClasses();
400
// Analyze metadata, but can't load actual classes
401
}
402
```
403
404
## Advanced Filtering and Processing
405
406
### Complex Queries with Streams
407
408
```java { .api }
409
// Find all public final classes with no-arg constructor
410
List<ClassInfo> utilityClasses = scanResult.getAllClasses()
411
.stream()
412
.filter(classInfo -> classInfo.isPublic() && classInfo.isFinal())
413
.filter(classInfo -> {
414
MethodInfoList constructors = classInfo.getDeclaredConstructorInfo();
415
return constructors.stream()
416
.anyMatch(constructor -> constructor.getParameterInfo().length == 0);
417
})
418
.collect(toList());
419
420
// Find all REST endpoints across the application
421
Map<String, List<String>> endpointMap = scanResult.getClassesWithAnnotation("org.springframework.web.bind.annotation.RestController")
422
.stream()
423
.flatMap(controller -> controller.getDeclaredMethodInfo().stream())
424
.filter(method -> method.hasAnnotation("org.springframework.web.bind.annotation.RequestMapping"))
425
.collect(groupingBy(
426
method -> method.getClassInfo().getName(),
427
mapping(method -> method.getName(), toList())
428
));
429
```
430
431
The ScanResult API provides powerful and flexible querying capabilities that enable deep introspection of your codebase structure, dependencies, and resources while maintaining type safety and performance.