Wrap an interface

The minimum viable runtime proxy — give the builder an interface and an implementation, get a proxy back.

Problem

You have an interface and a concrete implementation. You want a proxy you can hand to consumers of the interface, with no surprises in observed behaviour — the proxy must return the same results as the real instance.

This is the smallest possible runtime proxy, used as a sanity check and as the starting point for everything else.

Files involved

The interface and the implementation

testusage/src/test/java/.../InnerDemoInterface.java + InnerDemoClass.java View on GitHub ↗
public interface InnerDemoInterface {
  String doWork();
}

public class InnerDemoClass implements InnerDemoInterface, HasLogger {

  public InnerDemoClass() {
    logger().info("InnerDemoClass = init {}", System.nanoTime());
  }

  @Override
  public String doWork() {
    return "InnerDemoClass.doWork()";
  }
}

From proxybuilder tag 00.10.00

InnerDemoClass is a plain implementation of InnerDemoInterface. The HasLogger mixin gives it a logger; everything else is standard Java.

Wrapping with the builder

testusage/src/test/java/.../DynamicProxyBuilderTest.java#testCreateBuilder02 View on GitHub ↗
@Test
public void testCreateBuilder02() throws Exception {
  final DynamicProxyBuilder<InnerDemoInterface, InnerDemoClass> builder = DynamicProxyBuilder.createBuilder(
      InnerDemoInterface.class,
      InnerDemoClass.class,
      CreationStrategy.NONE);
  final InnerDemoInterface demoLogic = builder.build();
  Assertions.assertNotNull(demoLogic);
  final InnerDemoClass original = new InnerDemoClass();
  Assertions.assertEquals(demoLogic.doWork(), original.doWork());
}

From proxybuilder tag 00.10.00

DynamicProxyBuilder.createBuilder(...) takes the interface class, the implementation class and a CreationStrategy. CreationStrategy.NONE means “create the wrapped instance immediately” — no laziness. .build() returns a proxy that satisfies the interface and forwards every call to the real instance.

The two assertions in the test prove the contract: the proxy is not null, and proxy.doWork() returns the same string as original.doWork().

Run it

./mvnw -pl testusage test -Dtest=DynamicProxyBuilderTest#testCreateBuilder02

The test passes silently — there is no expected console output beyond Maven’s “BUILD SUCCESS”. For visible output of a proxy in action, see Add a pre-action.

What to take away

  • The proxy implements the interface — consumers cannot tell the difference.
  • The builder is a single fluent chain; you add behaviour by calling more builder methods before .build().
  • This minimal form has no observable side effect. It is the foundation — pre-actions, security rules and metrics layer on top.

Edit this page on GitHub ↗