Java runtime metadata analysis library that enables reverse querying of classpath metadata for type system introspection
—
Serialization capabilities for saving and loading scanned metadata in multiple formats including XML, JSON, and Java code generation. This enables persistent storage and sharing of metadata across different application runs and environments.
Base contract for all serialization implementations providing read and write operations.
/**
* Contract for serializing/deserializing Reflections metadata
*/
interface Serializer {
/** Read Reflections metadata from input stream */
Reflections read(InputStream inputStream);
/** Save Reflections metadata to file */
File save(Reflections reflections, String filename);
/** Prepare file with parent directories (static utility) */
static File prepareFile(String filename);
}XML format serialization for human-readable and widely compatible metadata storage.
/**
* XML serialization format implementation
*/
class XmlSerializer implements Serializer {
/** Default constructor */
XmlSerializer();
/** Read XML metadata from input stream */
Reflections read(InputStream inputStream);
/** Save metadata as XML file */
File save(Reflections reflections, String filename);
}JSON format serialization for lightweight and web-friendly metadata storage.
/**
* JSON serialization format implementation
*/
class JsonSerializer implements Serializer {
/** Default constructor */
JsonSerializer();
/** Read JSON metadata from input stream */
Reflections read(InputStream inputStream);
/** Save metadata as JSON file */
File save(Reflections reflections, String filename);
}Java source code generation for type-safe compile-time access to metadata.
/**
* Java source code generation for type-safe access
*/
class JavaCodeSerializer implements Serializer {
/** Default constructor */
JavaCodeSerializer();
/** Not supported - throws UnsupportedOperationException */
Reflections read(InputStream inputStream);
/** Generate Java interface source code with type-safe metadata access methods */
File save(Reflections reflections, String name);
}Built-in serialization methods in the main Reflections class for convenient saving and loading.
/**
* Built-in serialization methods in Reflections class
*/
class Reflections {
/** Save metadata to XML file (default serializer) */
File save(String filename);
/** Save metadata to file using specified serializer */
File save(String filename, Serializer serializer);
/** Collect metadata from input stream using specified serializer */
Reflections collect(InputStream inputStream, Serializer serializer);
/** Collect metadata from file using specified serializer */
Reflections collect(File file, Serializer serializer);
}Static methods for collecting pre-saved metadata from various sources and locations.
/**
* Static methods for collecting saved metadata
*/
class Reflections {
/** Collect saved XML metadata from META-INF/reflections/ with default filter */
static Reflections collect();
/** Collect saved metadata with custom package prefix and resource name filter */
static Reflections collect(String packagePrefix, Predicate<String> resourceNameFilter);
/** Collect saved metadata with custom serializer */
static Reflections collect(String packagePrefix, Predicate<String> resourceNameFilter, Serializer serializer);
}import org.reflections.Reflections;
import org.reflections.serializers.XmlSerializer;
// Create and configure Reflections
Reflections reflections = new Reflections("com.mycompany");
// Save to XML using default serializer
File xmlFile = reflections.save("target/reflections-metadata.xml");
// Save to XML using explicit serializer
File explicitXmlFile = reflections.save("target/metadata.xml", new XmlSerializer());
// Load from XML
Reflections loadedReflections = new XmlSerializer().read(new FileInputStream(xmlFile));
// Collect from file
Reflections collected = new Reflections().collect(xmlFile, new XmlSerializer());import org.reflections.serializers.JsonSerializer;
import java.io.FileInputStream;
// Save to JSON
JsonSerializer jsonSerializer = new JsonSerializer();
File jsonFile = reflections.save("target/reflections-metadata.json", jsonSerializer);
// Load from JSON
Reflections fromJson = jsonSerializer.read(new FileInputStream(jsonFile));
// Merge JSON data with existing Reflections
Reflections existing = new Reflections("com.mycompany.core");
Reflections merged = existing.collect(jsonFile, jsonSerializer);import org.reflections.serializers.JavaCodeSerializer;
// Generate Java interface with type-safe access methods
JavaCodeSerializer codeSerializer = new JavaCodeSerializer();
File javaFile = reflections.save("src/generated/java/ReflectionsMetadata", codeSerializer);
// This generates a Java interface like:
// public interface ReflectionsMetadata {
// Set<String> getSubTypesOf_com_mycompany_Service();
// Set<String> getTypesAnnotatedWith_org_springframework_stereotype_Component();
// // ... other generated methods
// }
// Note: JavaCodeSerializer.read() is not supported and throws UnsupportedOperationExceptionimport org.reflections.util.FilterBuilder;
// Collect all saved metadata from META-INF/reflections/
Reflections collected = Reflections.collect();
// Collect with package prefix filter
Reflections packageFiltered = Reflections.collect("com.mycompany",
resourceName -> resourceName.contains("reflections"));
// Collect with custom resource filter
Reflections customFiltered = Reflections.collect("",
new FilterBuilder().includePattern(".*-reflections\\.xml")::test);
// Collect using different serializer
Reflections jsonCollected = Reflections.collect("com.mycompany",
resourceName -> resourceName.endsWith(".json"),
new JsonSerializer());// Example build-time metadata generation and collection pattern
public class MetadataGenerator {
public static void main(String[] args) {
// Generate metadata during build
Reflections buildTimeReflections = new Reflections(new ConfigurationBuilder()
.forPackages("com.mycompany")
.setScanners(Scanners.values())
.filterInputsBy(new FilterBuilder()
.includePackage("com.mycompany")
.excludePackage("com.mycompany.test")));
// Save to build output directory
buildTimeReflections.save("target/classes/META-INF/reflections/myapp-reflections.xml");
// Also generate JSON version
buildTimeReflections.save("target/classes/META-INF/reflections/myapp-reflections.json",
new JsonSerializer());
// Generate type-safe access code
buildTimeReflections.save("src/generated/java/com/mycompany/ReflectionsMetadata",
new JavaCodeSerializer());
}
}
// Runtime collection (much faster than scanning)
public class RuntimeUsage {
public void usePreGeneratedMetadata() {
// Collect pre-generated metadata at runtime
Reflections runtime = Reflections.collect("META-INF/reflections/",
name -> name.contains("myapp-reflections"));
// Use normally - no scanning overhead
Set<Class<?>> services = runtime.get(SubTypes.of(Service.class).asClass());
}
}// Custom serialization workflow
public class CustomSerializationWorkflow {
public void saveMultipleFormats(Reflections reflections, String baseName) {
// Save in multiple formats
reflections.save(baseName + ".xml", new XmlSerializer());
reflections.save(baseName + ".json", new JsonSerializer());
try {
reflections.save(baseName + ".java", new JavaCodeSerializer());
} catch (UnsupportedOperationException e) {
// Handle JavaCodeSerializer read limitation
System.out.println("Java code generation completed, read not supported");
}
}
public Reflections loadFromMultipleSources() {
Reflections combined = new Reflections();
// Load and merge from different sources
try {
combined.merge(new XmlSerializer().read(
new FileInputStream("metadata1.xml")));
} catch (FileNotFoundException e) {
// Handle missing file
}
try {
combined.merge(new JsonSerializer().read(
new FileInputStream("metadata2.json")));
} catch (FileNotFoundException e) {
// Handle missing file
}
return combined;
}
}// Optimized metadata loading pattern
public class OptimizedMetadataLoading {
private static final String METADATA_LOCATION = "META-INF/reflections/";
private static Reflections cachedReflections;
public static synchronized Reflections getReflections() {
if (cachedReflections == null) {
// Try to load from pre-saved metadata first (fast)
try {
cachedReflections = Reflections.collect(METADATA_LOCATION,
name -> name.endsWith("-reflections.xml"));
if (!cachedReflections.getStore().isEmpty()) {
return cachedReflections;
}
} catch (Exception e) {
// Fall back to scanning if collection fails
}
// Fall back to runtime scanning (slower)
cachedReflections = new Reflections(new ConfigurationBuilder()
.forPackages("com.mycompany")
.setScanners(Scanners.SubTypes, Scanners.TypesAnnotated));
// Save for next time
try {
cachedReflections.save("target/generated-reflections.xml");
} catch (Exception e) {
// Ignore save errors
}
}
return cachedReflections;
}
}// Testing with serialized metadata
public class SerializationTest {
@Test
public void testMetadataSerialization() throws IOException {
// Create test metadata
Reflections original = new Reflections("com.mycompany.test");
// Save to temporary file
File tempFile = File.createTempFile("test-reflections", ".xml");
original.save(tempFile.getAbsolutePath());
// Load and compare
Reflections loaded = new XmlSerializer().read(new FileInputStream(tempFile));
// Verify metadata integrity
assertEquals(original.getStore().size(), loaded.getStore().size());
assertEquals(original.getSubTypesOf(TestInterface.class),
loaded.getSubTypesOf(TestInterface.class));
// Cleanup
tempFile.delete();
}
@Test
public void testJsonSerialization() throws IOException {
Reflections original = new Reflections("com.mycompany.test");
// Test JSON serialization round-trip
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
new JsonSerializer().save(original, outputStream.toString());
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
Reflections loaded = new JsonSerializer().read(inputStream);
// Verify equality
assertEquals(original.getStore(), loaded.getStore());
}
}// Robust serialization with error handling
public class RobustSerialization {
public boolean saveWithRetry(Reflections reflections, String filename, int maxRetries) {
for (int i = 0; i < maxRetries; i++) {
try {
reflections.save(filename);
return true;
} catch (Exception e) {
System.err.println("Save attempt " + (i + 1) + " failed: " + e.getMessage());
if (i == maxRetries - 1) {
e.printStackTrace();
}
}
}
return false;
}
public Reflections loadWithFallback(String primaryFile, String fallbackFile) {
// Try primary file first
try {
return new XmlSerializer().read(new FileInputStream(primaryFile));
} catch (Exception e) {
System.err.println("Failed to load primary metadata: " + e.getMessage());
}
// Try fallback file
try {
return new XmlSerializer().read(new FileInputStream(fallbackFile));
} catch (Exception e) {
System.err.println("Failed to load fallback metadata: " + e.getMessage());
}
// Fall back to runtime scanning
System.err.println("Falling back to runtime scanning");
return new Reflections("com.mycompany");
}
public void validateSerializedMetadata(Reflections reflections) throws IllegalStateException {
Store store = reflections.getStore();
if (store.isEmpty()) {
throw new IllegalStateException("Loaded metadata is empty");
}
// Validate expected scanners are present
if (!store.containsKey("SubTypes")) {
throw new IllegalStateException("SubTypes scanner data missing");
}
if (!store.containsKey("TypesAnnotated")) {
throw new IllegalStateException("TypesAnnotated scanner data missing");
}
// Validate data integrity
Map<String, Set<String>> subTypes = store.get("SubTypes");
if (subTypes.isEmpty()) {
System.err.println("Warning: No SubTypes data found");
}
}
}/**
* Exception thrown during serialization operations
*/
class ReflectionsException extends RuntimeException {
ReflectionsException(String message);
ReflectionsException(String message, Throwable cause);
ReflectionsException(Throwable cause);
}
/**
* File utility for serialization operations
*/
interface Serializer {
/** Static utility to prepare file with parent directories */
static File prepareFile(String filename) {
File file = new File(filename);
File parent = file.getAbsoluteFile().getParentFile();
if (parent != null && !parent.exists()) {
parent.mkdirs();
}
return file;
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-reflections--reflections