public class FastClasspathScanner extends Object
new FastClasspathScanner(
// Whitelisted package prefixes to scan:
new String[] { "com.xyz.widget", "com.xyz.gizmo" })
.matchSubclassesOf(DBModel.class,
// c is a subclass of DBModel or a descendant subclass
c -> System.out.println("Subclass of DBModel: " + c.getName()))
.matchSubinterfacesOf(Role.class,
// c is an interface that extends the interface Role
c -> System.out.println("Subinterface of Role: " + c.getName()))
.matchClassesImplementing(Runnable.class,
// c is a class that implements the interface Runnable; more precisely,
// c or one of its superclasses implements the interface Runnable, or
// implements an interface that is a descendant of Runnable
c -> System.out.println("Implements Runnable: " + c.getName()))
.matchClassesWithAnnotation(RestHandler.class,
// c is a class annotated with RestHandler
c -> System.out.println("Has a RestHandler class annotation: " + c.getName()))
.matchStaticFinalFieldNames(
Stream.of("com.xyz.Config.POLL_INTERVAL", "com.xyz.Config.LOG_LEVEL")
.collect(Collectors.toCollection(HashSet::new)),
// The following method is called when any static final fields with
// names matching one of the above fully-qualified names are
// encountered, as long as those fields are initialized to constant
// values. The value returned is the value in the classfile, not the
// value that would be returned by reflection, so this can be useful
// in hot-swapping of changes to static constants in classfiles if
// the constant value is changed and the class is re-compiled while
// the code is running. (Eclipse doesn't hot-replace static constant
// initializer values if you change them while running code in the
// debugger, so you can pick up changes this way instead).
// Note that the visibility of the fields is not checked; the value
// of the field in the classfile is returned whether or not it
// should be visible.
(String className, String fieldName, Object fieldConstantValue) ->
System.out.println("Static field " + fieldName + " of class "
+ className + " " + " has constant literal value "
+ fieldConstantValue + " in classfile"))
.matchFilenamePattern("^template/.\*\.html",
// templatePath is a path on the classpath that matches the above
// pattern; inputStream is a stream opened on the file or zipfile entry
// No need to close inputStream before exiting, it is closed by caller.
(absolutePath, relativePath, inputStream) -> {
try {
String template = IOUtils.toString(inputStream, "UTF-8");
System.out.println("Found template: " + absolutePath
+ " (size " + template.length() + ")");
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.scan(); // Actually perform the scan
// [...Some time later...]
// See if any timestamps on the classpath are more recent than the time of the
// previous scan. (Even faster than classpath scanning, because classfiles
// don't have to be opened.)
boolean classpathContentsModified =
fastClassPathScanner.classpathContentsModifiedSinceScan();
You can also get a list of matching fully-qualified classnames for interfaces and classes matching required criteria
without ever calling the classloader for the matching classes, e.g.:
FastClasspathScanner scanner = new FastClasspathScanner(
// Whitelisted package prefixes to scan:
new String[] { "com.xyz.widget" });
// Parse the class hierarchy of all classfiles on the classpath without calling the classloader for any of them
scanner.scan();
// Get the names of all subclasses of Widget on the classpath
List subclassesOfWidget = scanner.getSubclassesOf("com.xyz.widget.Widget");
Note that you need to pass a whitelist of package prefixes to scan into the constructor, and the ability to detect
that a class or interface extends another depends upon the entire ancestral path between the two classes or
interfaces having one of the whitelisted package prefixes.
When matching involves classfiles (i.e. in all cases except FastClasspathScanner#matchFilenamePattern, which deals
with arbitrary files on the classpath), if the same fully-qualified class name is encountered more than once on the
classpath, the second and subsequent definitions of the class are ignored.
The scanner also records the latest last-modified timestamp of any file or directory encountered, and you can see if
that latest last-modified timestamp has increased (indicating that something on the classpath has been updated) by
calling classpathContentsModifiedSinceScan(). This can be used to enable dynamic class-reloading if something on the
classpath is updated, for example to support hot-replace of route handler classes in a webserver.
classpathContentsModifiedSinceScan() is several times faster than the original call to scan(), since only
modification timestamps need to be checked.
Inspired by: https://github.com/rmuller/infomas-asl/tree/master/annotation-detector
See also: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
Please let me know if you find this useful!Constructor and Description |
---|
FastClasspathScanner(String[] packagesToScan)
Constructs a FastClasspathScanner instance.
|
Modifier and Type | Method and Description |
---|---|
boolean |
classpathContentsModifiedSinceScan()
Returns true if the classpath contents have been changed since scan() was last called.
|
<T> List<String> |
getClassesImplementing(Class<T> implementedInterface)
Returns a list of classes on the classpath that implement the specified interface or a subinterface, or whose
superclasses implement the specified interface or a sub-interface.
|
<T> List<String> |
getClassesImplementing(String implementedInterfaceName)
Returns a list of classes on the classpath that implement the specified interface or a subinterface, or whose
superclasses implement the specified interface or a sub-interface.
|
<T> List<String> |
getClassesWithAnnotation(Class<?> annotation)
Returns a list of classes on the classpath that have the specified annotation.
|
<T> List<String> |
getClassesWithAnnotation(String annotationName)
Returns a list of classes on the classpath that have the specified annotation.
|
<T> List<String> |
getSubclassesOf(Class<T> superclass)
Returns the list of classes on the classpath that extend the specified superclass.
|
<T> List<String> |
getSubclassesOf(String superclassName)
Returns the list of classes on the classpath that extend the specified superclass.
|
<T> List<String> |
getSubinterfacesOf(Class<T> superInterface)
Returns the list of interfaces on the classpath that extend a given superinterface.
|
<T> List<String> |
getSubinterfacesOf(String superInterfaceName)
Returns the list of interfaces on the classpath that extend a given superinterface.
|
static ArrayList<File> |
getUniqueClasspathElements()
Get a list of unique elements on the classpath (directories and files) as File objects, preserving order.
|
<T> FastClasspathScanner |
matchClassesImplementing(Class<T> implementedInterface,
InterfaceMatchProcessor<T> interfaceMatchProcessor)
Calls the provided InterfaceMatchProcessor for classes on the classpath that implement the specified interface or
a subinterface, or whose superclasses implement the specified interface or a sub-interface.
|
FastClasspathScanner |
matchClassesWithAnnotation(Class<?> annotation,
ClassAnnotationMatchProcessor classAnnotationMatchProcessor)
Calls the provided ClassMatchProcessor if classes are found on the classpath that have the specified annotation.
|
FastClasspathScanner |
matchFilenamePattern(String filenameMatchPattern,
FileMatchProcessor fileMatchProcessor)
Calls the given FileMatchProcessor if files are found on the classpath with the given regexp pattern in their
path.
|
FastClasspathScanner |
matchStaticFinalFieldNames(HashSet<String> fullyQualifiedStaticFinalFieldNames,
StaticFinalFieldMatchProcessor staticFinalFieldMatchProcessor)
Calls the given StaticFinalFieldMatchProcessor if classes are found on the classpath that contain static final
fields that match one of a set of fully-qualified field names, e.g.
|
<T> FastClasspathScanner |
matchSubclassesOf(Class<T> superclass,
SubclassMatchProcessor<T> subclassMatchProcessor)
Calls the provided SubclassMatchProcessor if classes are found on the classpath that extend the specified
superclass.
|
<T> FastClasspathScanner |
matchSubinterfacesOf(Class<T> superInterface,
SubinterfaceMatchProcessor<T> subinterfaceMatchProcessor)
Calls the provided SubInterfaceMatchProcessor if an interface that extends a given superinterface is found on the
classpath.
|
void |
scan()
Scans the classpath for matching files, and calls any match processors if a match is identified.
|
public FastClasspathScanner(String[] packagesToScan)
packagesToScan
- package prefixes to scan, e.g. new String[] { "com.xyz.widget", "com.xyz.gizmo" }public <T> FastClasspathScanner matchSubclassesOf(Class<T> superclass, SubclassMatchProcessor<T> subclassMatchProcessor)
superclass
- The superclass to match (i.e. the class that subclasses need to extend to match).subclassMatchProcessor
- the SubclassMatchProcessor to call when a match is found.public <T> List<String> getSubclassesOf(Class<T> superclass)
superclass
- The superclass to match (i.e. the class that subclasses need to extend to match).public <T> List<String> getSubclassesOf(String superclassName)
superclassName
- The name of the superclass to match (i.e. the name of the class that subclasses need to extend).public <T> FastClasspathScanner matchSubinterfacesOf(Class<T> superInterface, SubinterfaceMatchProcessor<T> subinterfaceMatchProcessor)
superInterface
- The superinterface to match (i.e. the interface that subinterfaces need to extend to match).subinterfaceMatchProcessor
- the SubinterfaceMatchProcessor to call when a match is found.public <T> List<String> getSubinterfacesOf(Class<T> superInterface)
superInterface
- The superinterface to match (i.e. the interface that subinterfaces need to extend to match).public <T> List<String> getSubinterfacesOf(String superInterfaceName)
superInterfaceName
- The name of the superinterface to match (i.e. the name of the interface that subinterfaces need to
extend).public <T> FastClasspathScanner matchClassesImplementing(Class<T> implementedInterface, InterfaceMatchProcessor<T> interfaceMatchProcessor)
implementedInterface
- The interface that classes need to implement.interfaceMatchProcessor
- the ClassMatchProcessor to call when a match is found.public <T> List<String> getClassesImplementing(Class<T> implementedInterface)
implementedInterface
- The interface that classes need to implement to match.public <T> List<String> getClassesImplementing(String implementedInterfaceName)
implementedInterfaceName
- The name of the interface that classes need to implement.public FastClasspathScanner matchClassesWithAnnotation(Class<?> annotation, ClassAnnotationMatchProcessor classAnnotationMatchProcessor)
annotation
- The class annotation to match.classAnnotationMatchProcessor
- the ClassAnnotationMatchProcessor to call when a match is found.public <T> List<String> getClassesWithAnnotation(Class<?> annotation)
annotation
- The class annotation.public <T> List<String> getClassesWithAnnotation(String annotationName)
annotationName
- The name of the class annotation.public FastClasspathScanner matchStaticFinalFieldNames(HashSet<String> fullyQualifiedStaticFinalFieldNames, StaticFinalFieldMatchProcessor staticFinalFieldMatchProcessor)
fullyQualifiedStaticFinalFieldNames
- The set of fully-qualified static field names to match.staticFinalFieldMatchProcessor
- the StaticFinalFieldMatchProcessor to call when a match is found.public FastClasspathScanner matchFilenamePattern(String filenameMatchPattern, FileMatchProcessor fileMatchProcessor)
filenameMatchPattern
- The regexp to match, e.g. "app/templates/.\*\.html"fileMatchProcessor
- The FileMatchProcessor to call when each match is found.public static ArrayList<File> getUniqueClasspathElements()
public void scan()
public boolean classpathContentsModifiedSinceScan()
Copyright © 2015. All rights reserved.