Skip to content

Latest commit

 

History

149 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🚀 OpenProxy

License Java JMH

中文版 | User Guide | Benchmark Results

A high-performance dynamic proxy library for Java. OpenProxy generates proxy classes at runtime with ASM and dispatches every intercepted call through a hashCode-driven switch with a direct INVOKESPECIAL super call — no reflection, no MethodHandle, JIT-inlinable. Interface proxies run at java.lang.reflect.Proxy parity; default methods are ~6.5× faster.

✨ Why OpenProxy

  • Direct super dispatchinvokeSuper compiles to a direct super.method(args); no reflection, no MethodHandle, JIT-inlinable.
  • Beats CGLib by ~3–5× on class proxies; interface proxies at java.lang.reflect.Proxy parity and ~6× faster on default methods.
  • One API for classes and interfacesOpenProxy.proxy(...) with generic type inference, no casts.
  • GC-safe — proxy classes use Lookup.defineHiddenClass(), so there is no ClassLoader leak.

📋 Features

Core

  • Unified OpenProxy.proxy(target, interceptor) entry point for classes and interfaces
  • Functional Interceptor API — a single-method interface, use a lambda
  • invokeSuper(proxy, method, args) for zero-overhead super dispatch
  • WeakCache-backed proxy-class caching keyed on the method-to-interceptor mapping

Selective interception

  • Group.of(predicate, interceptor) + Group.otherwise(...) — first-match-wins with zero hot-path overhead
  • Methods matching no group pass through with zero interception cost

Proxy capabilities

  • Interface proxy — runtime interface implementations without reflection
  • Multi-interface proxy — one object, several interfaces, with conflict detection
  • Non-public interface proxy — package-private interfaces, defined in the interface's own package
  • Constructor arguments — proxy classes without a no-arg constructor
  • Constructor interceptionConstructorInterceptor hooks before/after the superclass constructor, with argument rewriting and veto
  • Static method proxyproxyStatic returns a class shadowing public static methods
  • Annotation-driven API@Intercept / @Around declarative matching at lambda speed
  • Hot reload / hot swapevict / evictClassLoader for hot-deployed classes, rebind to swap interceptors on a live instance

⚡ Quick Start

Class proxy

Greeter proxy = OpenProxy.proxy(Greeter.class, (obj, method, args) -> {
    System.out.println("before " + method.getName());
    Object result = OpenProxy.invokeSuper(obj, method, args);
    System.out.println("after " + method.getName());
    return result;
});

String greeting = proxy.hello("World");
// before hello
// after hello
// greeting == "Hello, World"

Interface proxy

Calculator calc = OpenProxy.proxy(Calculator.class, (obj, method, args) -> {
    System.out.println("calling " + method.getName());
    return (int) args[0] + (int) args[1];
});

int result = calc.add(10, 20);   // 30

Method grouping

Greeter proxy = OpenProxy.proxy(Greeter.class,
        Group.of(m -> m.getName().startsWith("get"), getterInterceptor),
        Group.of(m -> m.getName().startsWith("set"), setterInterceptor),
        Group.otherwise(fallbackInterceptor));

Annotation-driven

@Intercept
class MetricsInterceptor {
    @Around("get*")
    Object measure(Object proxy, Method method, Object[] args) throws Throwable {
        return OpenProxy.invokeSuper(proxy, method, args);
    }
}

Greeter proxy = OpenProxy.intercept(Greeter.class, new MetricsInterceptor());

📊 Performance

JMH benchmarks on Java 25 (all scores in ns/op, lower is better). Full tables, methodology, and run instructions: docs/benchmark-results.md.

  • Class proxies beat CGLib by ~3–5× on scenarios with actual work; unmatched methods run at direct-call speed.
  • Interface proxies run at parity with java.lang.reflect.Proxy and are ~6× faster on default methods.
  • Multi-interceptor (Group) has byte-identical hot paths to the single-interceptor API — zero degradation.
  • Annotation-driven interception reaches hand-written-lambda parity.

🏗️ How it works

  1. OpenProxy.proxy(...) matches each proxyable method to an interceptor via a Group chain.
  2. A generator emits bytecode: one _interceptor$N field per distinct interceptor, one override per method, and a dispatch(Method, Object[]) method.
  3. On each call, the override boxes the arguments and calls Interceptor.intercept(...). If the interceptor calls invokeSuper, dispatch() branches on method.hashCode() and jumps straight to INVOKESPECIAL super.method(...).

The key insight: dispatch uses a deterministic Method.hashCode() to build an if-else chain whose branches are direct super calls — no reflection, no MethodHandle, fully JIT-inlinable. See the user guide for the full picture.

📋 Requirements

  • Java 25+
  • ASM 9.7.1 (compile dependency)

🧩 JPMS / Strong Encapsulation

Class proxies are defined in the target's package via MethodHandles.privateLookupIn. If the target lives in a strongly encapsulated module (any non-open package, including java.base packages such as java.util), proxy() fails fast with an actionable --add-opens hint. Interface proxies use a public lookup and support public interfaces only (same as java.lang.reflect.Proxy). See JPMS.

📦 Installation

git clone https://github.com/lamspace/openproxy.git
cd openproxy
mvn install -DskipTests

Maven Central publishing is in progress; until then, depend on the artifact from your local repository:

<dependency>
    <groupId>io.github.lamspace</groupId>
    <artifactId>openproxy</artifactId>
    <version>0.1.0-SNAPSHOT</version>
</dependency>

🆚 OpenProxy vs the alternatives

Feature OpenProxy CGLib java.lang.reflect.Proxy
Proxies concrete classes
Super-call mechanism direct INVOKESPECIAL MethodProxy + FastClass N/A
GC-safe (hidden class)
Selective interception Group.of CallbackFilter
Multi-interface proxy
Constructor interception
Static method proxy
Hot reload / rebind
Annotation-driven API
Functional API ✅ lambda
Maven Central Coming soon Built-in

📖 Documentation

📄 License

Apache License 2.0

About

The fast-path dynamic proxy for Java — runtime ASM codegen, zero reflection, JIT-inlinable super dispatch. 3–5× faster than CGLib.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages