The uber-fast, ultra-lightweight classpath and module scanner for JVM languages.
—
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.
import io.github.classgraph.ScanResult;
import io.github.classgraph.ClassInfo;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ResourceList;
import io.github.classgraph.MethodInfoList;
import io.github.classgraph.AnnotationInfo;
import java.util.Map;
import java.util.List;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Properties;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan()) {
// Get all classes found during scan
ClassInfoList allClasses = scanResult.getAllClasses();
// Get specific class by name
ClassInfo classInfo = scanResult.getClassInfo("com.example.MyClass");
// Get classes by type
ClassInfoList standardClasses = scanResult.getAllStandardClasses(); // Non-interface classes
ClassInfoList interfaces = scanResult.getAllInterfaces();
ClassInfoList annotations = scanResult.getAllAnnotations();
ClassInfoList enums = scanResult.getAllEnums();
ClassInfoList records = scanResult.getAllRecords(); // JDK 14+
// Combined queries
ClassInfoList interfacesAndAnnotations = scanResult.getAllInterfacesAndAnnotations();
// Get as Map for O(1) lookup
Map<String, ClassInfo> classMap = scanResult.getAllClassesAsMap();
}try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan()) {
// Find all subclasses (direct and indirect)
ClassInfoList subclasses = scanResult.getSubclasses("java.util.AbstractList");
ClassInfoList subclassesByClass = scanResult.getSubclasses(AbstractList.class);
// Find superclass hierarchy
ClassInfoList superclasses = scanResult.getSuperclasses("java.util.ArrayList");
ClassInfoList superclassesByClass = scanResult.getSuperclasses(ArrayList.class);
// Find interfaces of a class
ClassInfoList interfaces = scanResult.getInterfaces("java.util.ArrayList");
ClassInfoList interfacesByClass = scanResult.getInterfaces(ArrayList.class);
// Find all implementers of an interface
ClassInfoList implementers = scanResult.getClassesImplementing("java.util.List");
ClassInfoList implementersByClass = scanResult.getClassesImplementing(List.class);
}// Find all service implementations
ClassInfoList serviceImpls = scanResult.getClassesImplementing("com.example.Service");
// Find all classes extending a specific abstract class
ClassInfoList controllerSubclasses = scanResult.getSubclasses("com.example.AbstractController");
// Find all classes that implement multiple interfaces
ClassInfoList multiInterface = scanResult.getAllClasses()
.filter(classInfo ->
classInfo.implementsInterface("java.io.Serializable") &&
classInfo.implementsInterface("java.lang.Cloneable"));ClassInfoList supports set operations for combining and filtering results:
// Get two different sets of classes
ClassInfoList serviceClasses = scanResult.getClassesImplementing("com.example.Service");
ClassInfoList annotatedClasses = scanResult.getClassesWithAnnotation("com.example.Component");
// Union - classes that are in either list
ClassInfoList unionClasses = serviceClasses.union(annotatedClasses);
// Intersection - classes that are in both lists
ClassInfoList intersectionClasses = serviceClasses.intersect(annotatedClasses);
// Exclusion - classes in first list but not in second
ClassInfoList excludedClasses = serviceClasses.exclude(annotatedClasses);
// Multiple set operations
ClassInfoList controllers = scanResult.getClassesWithAnnotation("org.springframework.stereotype.Controller");
ClassInfoList restControllers = scanResult.getClassesWithAnnotation("org.springframework.web.bind.annotation.RestController");
ClassInfoList services = scanResult.getClassesWithAnnotation("org.springframework.stereotype.Service");
// All Spring components
ClassInfoList allSpringComponents = controllers.union(restControllers, services);try (ScanResult scanResult = new ClassGraph().enableAnnotationInfo().scan()) {
// Classes with specific annotation
ClassInfoList annotatedClasses = scanResult.getClassesWithAnnotation("javax.persistence.Entity");
ClassInfoList annotatedByClass = scanResult.getClassesWithAnnotation(Entity.class);
// Classes with ALL specified annotations
ClassInfoList withAllAnnotations = scanResult.getClassesWithAllAnnotations(
"javax.persistence.Entity", "javax.persistence.Table");
// Classes with ANY of the specified annotations
ClassInfoList withAnyAnnotations = scanResult.getClassesWithAnyAnnotation(
"javax.ws.rs.GET", "javax.ws.rs.POST", "javax.ws.rs.PUT");
// Get annotations on a specific class
ClassInfoList annotations = scanResult.getAnnotationsOnClass("com.example.MyClass");
}// Find classes that have methods with specific annotations
ClassInfoList classesWithMethodAnnotation = scanResult.getClassesWithMethodAnnotation("javax.ws.rs.GET");
// Find classes that have fields with specific annotations
ClassInfoList classesWithFieldAnnotation = scanResult.getClassesWithFieldAnnotation("javax.persistence.Column");
// Find classes with method parameter annotations
ClassInfoList classesWithParamAnnotation = scanResult.getClassesWithMethodParameterAnnotation("javax.validation.Valid");// Process REST endpoints
ClassInfoList restControllers = scanResult.getClassesWithAnnotation("org.springframework.web.bind.annotation.RestController");
for (ClassInfo controllerClass : restControllers) {
System.out.println("Controller: " + controllerClass.getName());
// Find all request mapping methods
MethodInfoList requestMethods = controllerClass.getDeclaredMethodInfo()
.filter(methodInfo ->
methodInfo.hasAnnotation("org.springframework.web.bind.annotation.RequestMapping") ||
methodInfo.hasAnnotation("org.springframework.web.bind.annotation.GetMapping") ||
methodInfo.hasAnnotation("org.springframework.web.bind.annotation.PostMapping"));
for (MethodInfo method : requestMethods) {
System.out.println(" Endpoint: " + method.getName());
// Extract annotation values
AnnotationInfo mapping = method.getAnnotationInfo("org.springframework.web.bind.annotation.GetMapping");
if (mapping != null) {
String[] paths = (String[]) mapping.getParameterValues().getValue("value");
System.out.println(" Paths: " + Arrays.toString(paths));
}
}
}try (ScanResult scanResult = new ClassGraph().acceptPaths("META-INF", "config").scan()) {
// Get all resources found
ResourceList allResources = scanResult.getAllResources();
Map<String, ResourceList> resourceMap = scanResult.getAllResourcesAsMap();
// Find resources by specific path
ResourceList configFiles = scanResult.getResourcesWithPath("META-INF/spring.factories");
// Find resources ignoring accept/reject filters
ResourceList allSpringFiles = scanResult.getResourcesWithPathIgnoringAccept("META-INF/spring.factories");
// Find by filename (leaf name)
ResourceList propertiesFiles = scanResult.getResourcesWithLeafName("application.properties");
// Find by extension
ResourceList jsonFiles = scanResult.getResourcesWithExtension("json");
ResourceList xmlFiles = scanResult.getResourcesWithExtension("xml");
}import java.util.regex.Pattern;
// Find resources matching regex pattern
Pattern logbackPattern = Pattern.compile(".*logback.*\\.xml");
ResourceList logbackConfigs = scanResult.getResourcesMatchingPattern(logbackPattern);
// Find resources matching wildcard pattern
ResourceList sqlFiles = scanResult.getResourcesMatchingWildcard("**/*.sql");
ResourceList configFiles = scanResult.getResourcesMatchingWildcard("**/config/**");// Process JSON configuration files
scanResult.getResourcesWithExtension("json")
.forEachByteArrayIgnoringIOException((resource, content) -> {
String json = new String(content, StandardCharsets.UTF_8);
System.out.println("Processing " + resource.getPath() + ": " + json.length() + " bytes");
// Parse JSON and process...
});
// Load properties files
scanResult.getResourcesWithExtension("properties")
.forEach(resource -> {
try (InputStream inputStream = resource.open()) {
Properties props = new Properties();
props.load(inputStream);
System.out.println("Loaded " + props.size() + " properties from " + resource.getPath());
} catch (IOException e) {
System.err.println("Failed to load " + resource.getPath() + ": " + e.getMessage());
}
});try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan()) {
// Get all packages
PackageInfoList allPackages = scanResult.getPackageInfo();
// Get specific package
PackageInfo packageInfo = scanResult.getPackageInfo("com.example");
if (packageInfo != null) {
// Get classes in package
ClassInfoList classesInPackage = packageInfo.getClassInfo();
// Get package annotations
AnnotationInfoList packageAnnotations = packageInfo.getAnnotationInfo();
// Package hierarchy
PackageInfo parent = packageInfo.getParent();
PackageInfoList children = packageInfo.getChildren();
}
}// Get all modules
ModuleInfoList allModules = scanResult.getModuleInfo();
// Get specific module
ModuleInfo moduleInfo = scanResult.getModuleInfo("java.base");
if (moduleInfo != null) {
// Get classes in module
ClassInfoList classesInModule = moduleInfo.getClassInfo();
// Get packages in module
PackageInfoList packagesInModule = moduleInfo.getPackageInfo();
// Module location and reference
URI location = moduleInfo.getLocation();
ModuleRef moduleRef = moduleInfo.getModuleRef();
}When enableInterClassDependencies() is enabled:
try (ScanResult scanResult = new ClassGraph()
.enableInterClassDependencies()
.acceptPackages("com.example")
.scan()) {
// Get class dependency map (what each class depends on)
Map<ClassInfo, ClassInfoList> dependencyMap = scanResult.getClassDependencyMap();
// Get reverse dependency map (what depends on each class)
Map<ClassInfo, ClassInfoList> reverseDependencyMap = scanResult.getReverseClassDependencyMap();
// Analyze specific class dependencies
ClassInfo serviceClass = scanResult.getClassInfo("com.example.UserService");
if (serviceClass != null) {
ClassInfoList dependencies = serviceClass.getClassDependencies();
System.out.println("UserService depends on " + dependencies.size() + " other classes");
// Find what depends on UserService
ClassInfoList dependents = reverseDependencyMap.get(serviceClass);
if (dependents != null) {
System.out.println(dependents.size() + " classes depend on UserService");
}
}
}try (ScanResult scanResult = new ClassGraph().scan()) {
// Check if classpath has been modified since scan
boolean modified = scanResult.classpathContentsModifiedSinceScan();
if (modified) {
System.out.println("Classpath has changed - consider rescanning");
// Get timestamp of latest modification
long lastModified = scanResult.classpathContentsLastModifiedTime();
System.out.println("Last modified: " + new Date(lastModified));
}
}// Load class by name with error handling
try {
Class<?> clazz = scanResult.loadClass("com.example.MyClass", false); // false = don't initialize
System.out.println("Loaded class: " + clazz.getName());
} catch (Exception e) {
System.err.println("Failed to load class: " + e.getMessage());
}
// Load and cast class with type safety
try {
Class<? extends Service> serviceClass = scanResult.loadClass("com.example.MyService", Service.class, false);
Service instance = serviceClass.getDeclaredConstructor().newInstance();
} catch (Exception e) {
System.err.println("Failed to instantiate service: " + e.getMessage());
}ClassInfoList serviceClasses = scanResult.getClassesImplementing("com.example.Service");
// Load all service classes safely
List<Class<? extends Service>> loadedClasses = serviceClasses.loadClasses(Service.class, false);
// Filter out classes that failed to load
List<Class<? extends Service>> validClasses = loadedClasses.stream()
.filter(Objects::nonNull)
.collect(toList());
System.out.println("Successfully loaded " + validClasses.size() + " service classes");try (ScanResult scanResult = new ClassGraph().enableAllInfo().scan()) {
// Serialize scan results to JSON
String json = scanResult.toJSON();
// Serialize with formatting
String prettyJson = scanResult.toJSON(2); // 2-space indentation
// Save to file for caching
Files.write(Paths.get("scan-results.json"), json.getBytes());
}
// Later, deserialize from JSON
String json = Files.readString(Paths.get("scan-results.json"));
try (ScanResult cachedResult = ScanResult.fromJSON(json)) {
// Use cached results (classes can't be loaded from deserialized results)
boolean fromCache = cachedResult.isObtainedFromDeserialization();
System.out.println("Using cached scan results: " + fromCache);
ClassInfoList allClasses = cachedResult.getAllClasses();
// Analyze metadata, but can't load actual classes
}// Find all public final classes with no-arg constructor
List<ClassInfo> utilityClasses = scanResult.getAllClasses()
.stream()
.filter(classInfo -> classInfo.isPublic() && classInfo.isFinal())
.filter(classInfo -> {
MethodInfoList constructors = classInfo.getDeclaredConstructorInfo();
return constructors.stream()
.anyMatch(constructor -> constructor.getParameterInfo().length == 0);
})
.collect(toList());
// Find all REST endpoints across the application
Map<String, List<String>> endpointMap = scanResult.getClassesWithAnnotation("org.springframework.web.bind.annotation.RestController")
.stream()
.flatMap(controller -> controller.getDeclaredMethodInfo().stream())
.filter(method -> method.hasAnnotation("org.springframework.web.bind.annotation.RequestMapping"))
.collect(groupingBy(
method -> method.getClassInfo().getName(),
mapping(method -> method.getName(), toList())
));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.
Install with Tessl CLI
npx tessl i tessl/maven-io-github-classgraph--classgraph