Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ronmamo/reflections

Java runtime metadata analysis
https://github.com/ronmamo/reflections

Last synced: about 2 months ago
JSON representation

Java runtime metadata analysis

Awesome Lists containing this project

README

        

*❗ Please note: Reflections library is currently NOT under active development or maintenance ❗*

*Thank you for your continuous support!

There are open issues and also workarounds. Release version will be considered incase contributing PR fixing the main issues.*

*Last released `org.reflections:reflections:0.10.2` (Oct 2021)*

----

# Java runtime metadata analysis

[![Build Status](https://travis-ci.org/ronmamo/reflections.svg?branch=master)](https://travis-ci.org/ronmamo/reflections)

Reflections scans and indexes your project's classpath metadata, allowing reverse transitive query of the type system on runtime.

Using Reflections you can query for example:
* Subtypes of a type
* Types annotated with an annotation
* Methods with annotation, parameters, return type
* Resources found in classpath
And more...

*Reflections was written in the spirit of [Scannotations](http://bill.burkecentral.com/2008/01/14/scanning-java-annotations-at-runtime/) library*

## Usage
Add Reflections dependency to your project:
```xml
# Maven

org.reflections
reflections
0.10.2

# Gradle
implementation 'org.reflections:reflections:0.10.2'
```

Create Reflections instance and use the query functions:
```java
Reflections reflections = new Reflections("com.my.project");

Set> subTypes =
reflections.get(SubTypes.of(SomeType.class).asClass());

Set> annotated =
reflections.get(SubTypes.of(TypesAnnotated.with(SomeAnnotation.class)).asClass());
```

Or using previous 0.9.x APIs, for example:

```java
Set> subTypes =
reflections.getSubTypesOf(SomeType.class);

Set> annotated =
reflections.getTypesAnnotatedWith(SomeAnnotation.class);
```

*Note that there are some breaking changes with Reflections 0.10+, along with performance improvements and more functional API, see below.*

### Scan
Creating Reflections instance requires [ConfigurationBuilder](https://ronmamo.github.io/reflections/org/reflections/util/ConfigurationBuilder.html), typically configured with packages and [Scanners](https://ronmamo.github.io/reflections/org/reflections/scanners/Scanners.html) to use:

```java
// typical usage: scan package with the default scanners SubTypes, TypesAnnotated
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.forPackage("com.my.project")
.filterInputsBy(new FilterBuilder().includePackage("com.my.project")));
```

Other examples:
```java
import static org.reflections.scanners.Scanners.*;

// scan package with specific scanners
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.forPackage("com.my.project")
.filterInputsBy(new FilterBuilder().includePackage("com.my.project").excludePackage("com.my.project.exclude"))
.setScanners(TypesAnnotated, MethodsAnnotated, MethodsReturn));
```

Note that:
* **Scanner must be configured in order to be queried, otherwise an empty result is returned**
If not specified, default scanners will be used SubTypes, TypesAnnotated.
For all standard scanners use `Scanners.values()`. See more scanners in the source [package](https://ronmamo.github.io/reflections/org/reflections/scanners).
* **All relevant URLs should be configured**
Consider `.filterInputsBy()` in case too many classes are scanned.
If required, Reflections will [expand super types](https://ronmamo.github.io/reflections/org/reflections/Reflections.html#expandSuperTypes(java.util.Map)) in order to get the transitive closure metadata without scanning large 3rd party urls.
* Classloader can optionally be used for resolving runtime classes from names.

### Query
Once Reflections was instantiated and scanning was successful, it can be used for querying the indexed metadata.

```java
import static org.reflections.scanners.Scanners.*;

// SubTypes
Set> modules =
reflections.get(SubTypes.of(Module.class).asClass());

// TypesAnnotated (*1)
Set> singletons =
reflections.get(TypesAnnotated.with(Singleton.class).asClass());

// MethodsAnnotated
Set resources =
reflections.get(MethodsAnnotated.with(GetMapping.class).as(Method.class));

// FieldsAnnotated
Set ids =
reflections.get(FieldsAnnotated.with(Id.class).as(Field.class));

// Resources
Set properties =
reflections.get(Resources.with(".*\\.properties"));
```

More scanners:

```java
// MethodsReturn
Set voidMethods =
reflections.get(MethodsReturn.with(void.class).as(Method.class));

// MethodsSignature
Set someMethods =
reflections.get(MethodsSignature.of(long.class, int.class).as(Method.class));

// MethodsParameter
Set pathParam =
reflections.get(MethodsParameter.of(PathParam.class).as(Method.class));

// ConstructorsAnnotated
Set injectables =
reflections.get(ConstructorsAnnotated.with(Inject.class).as(Constructor.class));

// ConstructorsSignature
Set someConstructors =
reflections.get(ConstructorsSignature.of(String.class).as(Constructor.class));

// MethodParameterNamesScanner
List parameterNames =
reflections.getMemberParameterNames(member);

// MemberUsageScanner
Set usages =
reflections.getMemberUsages(member)
```

*See more examples in [ReflectionsQueryTest](src/test/java/org/reflections/ReflectionsQueryTest.java).*

*Note that previous 0.9.x APIs are still supported*

Compare Scanners and previous 0.9.x API (*)

| Scanners | previous 0.9.x API | previous Scanner |
| -------- | ------------------ | ------ |
| `get(SubType.of(T))` | getSubTypesOf(T) | ~~SubTypesScanner~~ |
| `get(SubTypes.of(`
    `TypesAnnotated.with(A)))` | getTypesAnnotatedWith(A) *(1)*| ~~TypeAnnotationsScanner~~ |
| `get(MethodsAnnotated.with(A))` | getMethodsAnnotatedWith(A) | ~~MethodAnnotationsScanner~~ |
| `get(ConstructorsAnnotated.with(A))` | getConstructorsAnnotatedWith(A) *(2)*| ~~MethodAnnotationsScanner~~ |
| `get(FieldsAnnotated.with(A))` | getFieldsAnnotatedWith(A) | ~~FieldAnnotationsScanner~~ |
| `get(Resources.with(regex))` | getResources(regex) | ~~ResourcesScanner~~ |
| `get(MethodsParameter.with(P))` | getMethodsWithParameter(P) *(3)*
~~getMethodsWithAnyParamAnnotated(P)~~| ~~MethodParameterScanner~~
*obsolete* |
| `get(MethodsSignature.of(P, ...))` | getMethodsWithSignature(P, ...) *(3)
~~getMethodsMatchParams(P, ...)~~*| " |
| `get(MethodsReturn.of(T))` | getMethodsReturn(T) *(3)*| " |
| `get(ConstructorsParameter.with(P))` | getConstructorsWithParameter(P) *(3)
~~getConstructorsWithAnyParamAnnotated(P)~~*| " |
| `get(ConstructorsSignature.of(P, ...))` | getConstructorsWithSignature(P, ...) *(3)
~~getConstructorsMatchParams(P, ...)~~*| " |

*Note: `asClass()` and `as()` mappings were omitted*

*(1): The equivalent of `getTypesAnnotatedWith(A)` is `get(SubTypes.of(TypesAnnotated.with(A)))`, including SubTypes*

*(2): MethodsAnnotatedScanner does not include constructor annotation scanning, use instead Scanners.ConstructorsAnnotated*

*(3): MethodParameterScanner is obsolete, use instead as required:
Scanners.MethodsParameter, Scanners.MethodsSignature, Scanners.MethodsReturn, Scanners.ConstructorsParameter, Scanners.ConstructorsSignature*

## ReflectionUtils
Apart from scanning classpath metadata using [Javassist](https://github.com/jboss-javassist/javassist),
Java Reflection convenient methods are available using
[ReflectionsUtils](https://ronmamo.github.io/reflections/org/reflections/ReflectionUtils.html):

```java
import static org.reflections.ReflectionUtils.*;

Set> superTypes = get(SuperTypes.of(T));
Set fields = get(Fields.of(T));
Set constructors = get(Constructors.of(T));
Set methods = get(Methods.of(T));
Set resources = get(Resources.with(T));

Set annotations = get(Annotations.of(T));
Set> annotationTypes = get(AnnotationTypes.of(T));
```

*Previous ReflectionUtils 0.9.x API is still supported though marked for removal, more info in the javadocs.*

## Query API
Each Scanner and ReflectionUtils function implements [QueryBuilder](https://ronmamo.github.io/reflections/org/reflections/util/QueryBuilder.html), and supports:
* `get()` - function returns direct values
* `with()` or `of()` - function returns all transitive values

*For example, `Scanners.SubTypes.get(T)` return direct subtypes,
while `Scanners.SubTypes.of(T)` return transitive subtypes hierarchy.
Same goes for `Scanners.TypesAnnotated` and `ReflectionUtils.SuperTypes` etc.*

Next, each function implements [QueryFunction](https://ronmamo.github.io/reflections/org/reflections/util/QueryFunction.html),
and provides fluent functional interface for composing `filter()`, `map()`, `flatMap()`, `as()` and more, such that:

```java
// filter, as/map
QueryFunction getters =
Methods.of(C1.class)
.filter(withModifier(Modifier.PUBLIC))
.filter(withPrefix("get").and(withParametersCount(0)))
.as(Method.class);

// compose Scanners and ReflectionUtils functions
QueryFunction methods =
SubTypes.of(type).asClass() // <-- classpath scanned metadata
.flatMap(Methods::of); // <-- java reflection api

// function of function
QueryFunction> queryAnnotations =
Annotations.of(Methods.of(C4.class))
.map(Annotation::annotationType);
```

See more in [ReflectionUtilsQueryTest](https://github.com/ronmamo/reflections/tree/master/src/test/java/org/reflections/ReflectionUtilsQueryTest.java)

A more complex example demonstrates getting merged annotations of rest controllers endpoints:
```java
// get all annotations of RequestMapping hierarchy (GetMapping, PostMapping, ...)
Set> metaAnnotations =
reflections.get(TypesAnnotated.getAllIncluding(RequestMapping.class.getName()).asClass());

QueryFunction> queryAnnotations =
// get all controller endpoint methods
MethodsAnnotated.with(metaAnnotations).as(Method.class)
.map(method ->
// get both method's + declaring class's RequestMapping annotations
get(Annotations.of(method.getDeclaringClass())
.add(Annotations.of(method))
.filter(a -> metaAnnotations.contains(a.annotationType())))
.stream()
// merge annotations' member values into a single hash map
.collect(new AnnotationMergeCollector(method)));

// apply query and map merged hashmap into java annotation proxy
Set mergedAnnotations =
reflections.get(mergedAnnotation
.map(map -> ReflectionUtils.toAnnotation(map, metaAnnotation)));
```

Check the [tests](src/test/java/org/reflections) folder for more examples and API usage

### What else?
- **Integrating with build lifecycle**
It is sometime useful to `Reflections.save()` the scanned metadata into xml/json as part of the build lifecycle for generating resources,
and then collect it on bootstrap with `Reflections.collect()` and avoid scanning. *See [reflections-maven](https://github.com/ronmamo/reflections-maven/) for example*.
- [JavaCodeSerializer](https://ronmamo.github.io/reflections/org/reflections/serializers/JavaCodeSerializer.html) - scanned metadata can be persisted into a generated Java source code.
Although less common, it can be useful for accessing types and members in a strongly typed manner. *(see [example](src/test/java/org/reflections/MyTestModelStore.java))*
- [AnnotationMergeCollector](https://ronmamo.github.io/reflections/org/reflections/util/AnnotationMergeCollector.html) - can be used to merge similar annotations. *(see [test](src/test/java/org/reflections/ReflectionUtilsQueryTest.java#L216))*
- `MemberUsageScanner` - experimental scanner allow querying for member usages `getMemberUsages()` of packages/types/elements in the classpath.
Can be used for finding usages between packages, layers, modules, types etc.

----

*Spread the spirit of open-source and collaboration, clean code and simplicity*