CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-github-classgraph--classgraph

The uber-fast, ultra-lightweight classpath and module scanner for JVM languages.

Pending
Overview
Eval results
Files

querying.mddocs/

Querying and Results

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.

Basic Result Access

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;

Class Information Queries

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

Inheritance and Implementation Queries

Subclass and Superclass Queries

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

Complex Inheritance Examples

// 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"));

Set Operations on ClassInfoList

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

Annotation Queries

Basic Annotation Queries

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");
}

Method and Field Annotation Queries

// 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");

Advanced Annotation Processing

// 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));
        }
    }
}

Resource Queries

Basic Resource Access

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");
}

Pattern-Based Resource Queries

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/**");

Resource Content Processing

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

Package and Module Information

Package Queries

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

Module Queries (JPMS)

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

Dependency Analysis

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");
        }
    }
}

Change Detection and Monitoring

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

Class Loading from Results

Safe Class Loading

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

Bulk Class Loading

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

Serialization and Persistence

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
}

Advanced Filtering and Processing

Complex Queries with Streams

// 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

docs

class-info.md

index.md

querying.md

resources.md

scanning.md

type-signatures.md

tile.json