Ecosyste.ms: Awesome

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

Awesome Lists | Featured Topics | Projects

https://github.com/flmelody/reflections

Reflections - Java runtime metadata analysis
https://github.com/flmelody/reflections

Last synced: about 1 month ago
JSON representation

Reflections - Java runtime metadata analysis

Awesome Lists containing this project

README

        

A fork of [`ronmamo/reflections`](https://github.com/ronmamo/reflections) use [`WTFPL license`](http://www.wtfpl.net/)
only

----

# Java runtime metadata analysis

![GitHub](https://img.shields.io/github/license/Flmelody/reflections)
![Dynamic XML Badge](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Frepo1.maven.org%2Fmaven2%2Forg%2Fflmelody%2Freflections%2Fmaven-metadata.xml&query=%2F%2Fmetadata%2Fversioning%2Flatest&label=maven-central)
![Java Version(8-17)](https://github.com/Flmelody/reflections/actions/workflows/maven-ci.yml/badge.svg)

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:

# Maven

```xml

org.flmelody
reflections
0.10.2

```

# Gradle

```groovy
implementation 'org.flmelody: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*