Open-source project
jOOQ/jOOR avatar
jOOQ/jOOR

jOOR: Fluent Reflection for Java

jOOR - Fluent Reflection in Java jOOR is a very simple fluent API that gives access to your Java Class structures in a more intuitive way. The JDK's reflection APIs are hard and verbose to use. Other languages have much simpler constructs to access type meta information at runtime. Let us make Java reflection better.

2,824 stars379 forksJavaApache-2.0

At a glance

What is it?
jOOR is a small Apache-2.0 licensed wrapper around java.lang.reflect for Java developers who want a shorter, fluent API for runtime reflection. It supports reflective object access, proxies, and optional runtime compilation, with separate artifacts for Java 9+, Java 8+, and Java 6+.
Who is it for?
Java developers should consider jOOR when reflective code is short-lived infrastructure code where the reduced syntax improves readability. They should not choose it when direct control over checked exceptions, exact reflection APIs, or dependency policy matters more than the compact expression.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 9 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why jOOR exists for Java reflection code

jOOR addresses the verbosity of the JDK reflection API. The README describes it as a fluent API over java.lang.reflect, intended to make access to Java class structures more intuitive at runtime. The target user is a Java developer who needs to load classes, instantiate objects, invoke methods, inspect or access fields, or work with reflective proxies without repeatedly writing the longer reflection calls.

The problem is specific. Standard reflection often turns a simple object operation into several statements involving Method objects, getMethod or getDeclaredMethod, invoke, casts, and checked exception handling. jOOR keeps that underlying mechanism but organizes the work as a chain. In the README's comparison, a sequence such as getEmployees, getAddress, and getStreet becomes a series of calls on wrapped objects. The resulting code follows the data path more closely.

This is a convenience library, not a different reflection model. The README calls jOOR a simple wrapper for java.lang.reflect, so its behavior remains tied to JVM reflection rules, including accessibility, signatures, argument matching, and exceptions. The library does not remove the need to understand those rules. It mainly changes the surface syntax used to apply them.

jOOR's fluent object and class access model

The core entry point is the static Reflect API. The example uses the static import onClass("java.lang.String"), which behaves like Class.forName(). The returned reflection object then creates a String instance, calls substring with an integer argument, calls toString, and returns the wrapped value through get().

The chain expresses three separate operations. Class resolution happens first. Constructor selection happens through create. Method lookup and invocation happen through call. get unwraps the result. Because the example returns a String from the final call, the result is a normal Java String rather than a reflection descriptor.

The API also accepts an existing object through on, as shown by the department and employee example. That distinction matters. onClass starts from a class name, while on starts from an object whose class can be inspected. The object-based form is convenient for navigating a graph whose concrete classes may not be known statically, such as a method chain reached through an interface or inherited result.

The README does not document every overload or every reflection member. It does show that calls can be composed and that jOOR selects the most specific matching constructor or method. That selection still depends on the JVM's available signatures and the arguments passed at runtime.

Installing jOOR for Java 9, Java 8, and Java 6

The README lists no external dependencies and provides Maven coordinates for three runtime targets. Choose the artifact that matches the Java version used by the consuming project. The Java 9+ artifact is named joor, while older compatibility lines have separate names.

For a Java 9+ Maven project, add this dependency:

xml
<dependency>
  <groupId>org.jooq</groupId>
  <artifactId>joor</artifactId>
  <version>0.9.16</version>
</dependency>

The reader should expect Maven to resolve org.jooq:joor:0.9.16 for a Java 9 or later project. The README states that jOOR has no dependencies, so no additional dependency block is required for that artifact.

For a Java 8+ project, the README specifies a different artifact:

xml
<dependency>
  <groupId>org.jooq</groupId>
  <artifactId>joor-java-8</artifactId>
  <version>0.9.16</version>
</dependency>

For Java 6+, use the compatibility artifact documented by the README:

xml
<dependency>
  <groupId>org.jooq</groupId>
  <artifactId>joor-java-6</artifactId>
  <version>0.9.16</version>
</dependency>

The version shown in the README is 0.9.16. The repository also contains directories named jOOR, jOOR-java-8, and jOOR-java-6, which corresponds with the separate artifacts described in the download section.

First reflective call with jOOR

The README's simplest example begins with a static import and then resolves, constructs, invokes, and unwraps a String. The following is the complete example as presented:

java
import static org.joor.Reflect.*;

String world = onClass("java.lang.String")
                .create("Hello World")
                .call("substring", 6)
                .call("toString")
                .get();

The first call loads the class by name. create("Hello World") selects a matching String constructor. call("substring", 6) invokes the matching method with the supplied argument. call("toString") invokes the no-argument method, and get returns the wrapped result.

For an object already available in the program, the README uses on instead:

java
Employee[] employees = on(department).call("getEmployees").get();

for (Employee employee : employees) {
  Street street = on(employee).call("getAddress").call("getStreet").get();
  System.out.println(street);
}

This form is useful when the starting point is an instance rather than a class name. The final type is still obtained through get(), so the example assigns the result to Employee[] or Street. The README does not document whether the wrapper preserves compile-time generic type information beyond the source-level cast or assignment, so consumers should not infer that every generic result is automatically type safe.

Proxy support through the as abstraction

jOOR also exposes java.lang.reflect.Proxy through the as abstraction. The README defines a local interface named StringProxy with one substring method, then wraps a String instance in that interface. The example is:

java
public interface StringProxy {
  String substring(int beginIndex);
}

String substring = onClass("java.lang.String")
                    .create("Hello World")
                    .as(StringProxy.class)
                    .substring(6);

The mechanism has two parts. First, jOOR creates the String object through its normal reflective class and constructor operations. Then as(StringProxy.class) creates a proxy for the wrapped object according to the supplied interface. The final call is an ordinary interface method call in source code, while the proxy handles the invocation routing.

This is different from merely using an interface at compile time. The object being wrapped still comes from a class resolved and instantiated reflectively, but the caller receives an interface-shaped view. The README does not describe invocation handlers, default method behavior, checked exception policy, or restrictions around proxy interfaces, so those details would need to be checked against the relevant JDK and library API documentation before use.

Optional runtime compilation through java.compiler

The project includes an optional feature for compiling Java source at runtime through javax.tools.JavaCompiler. The README states that this capability depends on the java.compiler module. It is not part of the basic dependency-free reflection wrapper unless the runtime environment supplies the required compiler module.

The documented API passes a class name and source text to Reflect.compile, creates the compiled object, and gets its value:

java
Supplier<String> supplier = Reflect.compile(
    "com.example.HelloWorld",
    "package com.example;\n" +
    "class HelloWorld implements java.util.function.Supplier<String> {\n" +
    "    public String get() {\n" +
    "        return \"Hello World!\";\n" +
    "    }\n" +
    "}\n").create().get();

System.out.println(supplier.get());

The README states that this prints "Hello World!". The important boundary is that runtime compilation is optional and tied to java.compiler. Environments without that module may not provide the required compiler services. This feature also changes the operational shape of an application because source text is compiled at runtime rather than packaged as a normal class file. The README does not document classpath handling, diagnostics, generated-class caching, or security policy requirements, so those are outside what can be confirmed here.

Limitations compared with standard reflection and other tools

The clearest limitation is reduced visibility into the underlying reflection operation. The standard java.lang.reflect API exposes Method and related objects directly. That gives a caller a place to inspect the selected member, work with its annotations and parameters, or handle particular checked exceptions. The jOOR examples hide those intermediate objects inside a fluent expression. This makes short chains easier to read, but it can make debugging or precise control less direct when a reflective operation fails.

jOOR also cannot bypass ordinary Java restrictions. A missing constructor, an argument that does not match a method signature, an inaccessible member, or a class that cannot be resolved will still constrain the operation. The README says that jOOR selects the most specific matching constructor or method, but it does not explain the full conflict-resolution rules.

For type-system or compile-time safety, standard Java code is usually the better choice. jOOR is most relevant when the class or member must be selected dynamically. It is also less suitable when a build system rejects dynamic compilation, when the runtime excludes java.compiler, or when the application needs explicit exception handling around every reflective step. The library changes syntax, not the fundamental limitations of JVM reflection.

jOOR versus FEST, Mirror, and direct reflection

The README identifies two alternatives. FEST offers everyday Java reflection with a fluent interface, while Mirror presents reflection modelled as XPath. These are different approaches to the same broad category. A fluent API keeps the shape of calls similar to jOOR, whereas an XPath model describes paths through an object graph and can be useful when navigation itself is the main requirement.

Direct java.lang.reflect is the more important comparison for adoption. It is part of the JDK, offers direct access to Method, Field, Constructor, and related objects, and does not add a project dependency. jOOR instead trades some of that explicit access for shorter method chains and a more direct reading of object navigation. Its dependency-free Maven coordinates are simple, but the artifact selection is split by Java version.

The project name is inspired by jOOQ, but the README's comparison is about the ergonomics of a fluent API, not SQL construction. jOOR is therefore not a replacement for direct reflection in every case. It is a syntax layer for reflective operations, with optional proxy and runtime compilation features that extend beyond the basic wrapper.

Maintenance status, license, and upgrade implications

The repository is not archived. Its last push was on 2026-10-14, and the listed release is version-0.9.16 from 2026-10-12. The README documents that version as the dependency version for Java 9+, Java 8+, and Java 6+ artifacts. This is the maintenance information available from the repository record, so it does not establish guarantees about future releases or compatibility beyond the documented artifact split.

The repository is licensed under Apache-2.0. That license permits use and modification under its terms, but this article does not provide legal advice. Teams should review LICENSE.txt for the obligations that apply to their distribution and use.

An upgrade can affect more than one Maven coordinate. A project must decide whether it stays on the artifact for its Java compatibility target, and changes in the separately named jOOR, jOOR-java-8, and jOOR-java-6 modules should be checked before changing the version. The README does not provide a migration guide, compatibility matrix, or changelog, so the release notes and source repository would be the relevant places to verify upgrade behavior.

Editorial conclusion

Java developers should consider jOOR when reflective code is short-lived infrastructure code where the reduced syntax improves readability. They should not choose it when direct control over checked exceptions, exact reflection APIs, or dependency policy matters more than the compact expression. Before adoption, verify the correct artifact for the project's Java version, confirm that the required reflective calls are permitted by its class and module boundaries, and inspect how exceptions from wrapped calls should be handled.

Frequently asked questions

Which jOOR Maven artifact should a Java 9 project use?

The README specifies org.jooq:joor with version 0.9.16 for Java 9 and later. Java 8+ projects use org.jooq:joor-java-8, while Java 6+ projects use org.jooq:joor-java-6.

Does jOOR require external dependencies?

The README states "Dependencies None!" for the core artifact. Its optional runtime compilation feature does, however, require the java.compiler module and access to javax.tools.JavaCompiler.

Can jOOR create a proxy for a reflectively created object?

Yes. The README demonstrates creating a String with onClass and create, then calling as(StringProxy.class) to expose it through a local interface before calling substring(6).

Official sources

  1. jOOQ/jOOR on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jooq-joor.svg)](https://hysenlabs.com/projects/jooq-joor)

Was this useful?