Skip to content

Add switchable Byte Buddy engine alongside Javassist - #18

Open
martinaldrin wants to merge 1 commit into
Ericsson:masterfrom
martinaldrin:byte-buddy-engine
Open

martinaldrin wants to merge 1 commit into
Ericsson:masterfrom
martinaldrin:byte-buddy-engine

Conversation

@martinaldrin

Copy link
Copy Markdown

Proxy can now generate its proxies with either Javassist or Byte Buddy. Javassist stays the default and its code path is unchanged.

Select the engine with -Dproxy.engine=javassist|bytebuddy, or at runtime with ProxyConfiguration.setEngine/setThreadEngine/callWithEngine.

The public API is untouched: no existing test file was modified, and the whole suite now runs twice, once per engine, from a second surefire execution. 222 tests pass on both engines on JDK 11, 17, 21 and 26.

Design notes:

  • The engine is behind a ProxyEngineFactory SPI, resolved lazily by ProxyEngineProvider, so byte-buddy classes are never loaded unless the byte-buddy engine is actually selected.
  • The earlier attempt at byte-buddy support used InvocationHandlerAdapter, which cannot reach the original implementation and therefore broke Invocation.invoke(). This uses MethodDelegation with @supermethod(nullIfImpossible = true) instead, which is the direct equivalent of javassist's proceed method.
  • Proxy classes are named into the proxied type's package and defined via MethodHandles.privateLookupIn, so package private types, interfaces and methods keep working. Class injection is only a fallback.
  • The per instance interceptor stack lives in a private synthetic field. It must not be public, or Util.isClassSafeFromPublicVariableProblems would silently refuse mdcLogging and recursiveIntercept on a proxy.
  • Generated classes are shared through a byte-buddy TypeCache keyed on type names only. Without it every proxy instance got its own class: 5000 proxies cost 3219 loaded classes, now it is 1218.

Proxy can now generate its proxies with either Javassist or Byte Buddy.
Javassist stays the default and its code path is unchanged.

Select the engine with -Dproxy.engine=javassist|bytebuddy, or at runtime
with ProxyConfiguration.setEngine/setThreadEngine/callWithEngine.

The public API is untouched: no existing test file was modified, and the
whole suite now runs twice, once per engine, from a second surefire
execution. 222 tests pass on both engines on JDK 11, 17, 21 and 26.

Design notes:
- The engine is behind a ProxyEngineFactory SPI, resolved lazily by
  ProxyEngineProvider, so byte-buddy classes are never loaded unless the
  byte-buddy engine is actually selected.
- The earlier attempt at byte-buddy support used InvocationHandlerAdapter,
  which cannot reach the original implementation and therefore broke
  Invocation.invoke(). This uses MethodDelegation with
  @supermethod(nullIfImpossible = true) instead, which is the direct
  equivalent of javassist's proceed method.
- Proxy classes are named into the proxied type's package and defined via
  MethodHandles.privateLookupIn, so package private types, interfaces and
  methods keep working. Class injection is only a fallback.
- The per instance interceptor stack lives in a private synthetic field.
  It must not be public, or Util.isClassSafeFromPublicVariableProblems
  would silently refuse mdcLogging and recursiveIntercept on a proxy.
- Generated classes are shared through a byte-buddy TypeCache keyed on
  type names only. Without it every proxy instance got its own class:
  5000 proxies cost 3219 loaded classes, now it is 1218.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant