ProxyGenerator constructor

ProxyGenerator()

Proxy Generator

Generates runtime proxy subclasses for all classes annotated with JetLeaf stereotypes such as:

  • @Service
  • @Component
  • @Configuration
  • @AutoConfiguration
  • @Repository
  • @Controller

The proxies produced by this generator form the foundation of JetLeaf’s AOP interception system, enabling runtime interception, tracing, and behavior modification on user-defined services.


Overview

The ProxyGenerator operates as a build_runner code generator that:

  1. Scans Dart libraries for eligible annotated classes.
  2. Verifies extendability (non-final, non-private).
  3. Collects all import dependencies (both direct and transitive) for correct type alias resolution.
  4. Emits a new class prefixed with $$, e.g.:
@Service()
class UserService { ... }

// Auto-generated:
final class $$UserService with Interceptable implements UserService, ClassGettable<UserService> {
  final UserService delegate;

  $$UserService(this.delegate);

  @override
  Future<User> findUser(String id) async => when<User>(
    () async => delegate.findUser(id),
    'findUser',
    MethodArguments(positionalArgs: [id]),
  );
}

Each generated proxy class:

  • Implements both the target class and ClassGettable<T>.
  • Mixes in Interceptable to delegate method calls through MethodInterceptorDispatcher.
  • Forwards properties and methods to the delegate object.
  • Wraps interceptable methods with when() for interception.

Generation Workflow

  1. Annotation Scan

    • Filters classes with JetLeaf stereotype annotations.
    • Ensures they are not final or from an external package.
  2. Import and Alias Construction

    • Captures the original source file’s imports.
    • Adds imports for JetLeaf core libraries:
      • package:jetleaf_core/intercept.dart
      • package:jetleaf_lang/lang.dart
    • Generates URI aliases to avoid import name collisions.
  3. Proxy Emission

    • Emits proxy class definitions with:
      • Correct import aliasing
      • Generated headers
      • Wrapped delegates
      • Method overrides supporting interception
  4. Method Generation

    • Each method override uses:
      • this.when<T>(...) if return type is async/interceptable
      • Direct delegate.method(...) otherwise
  5. Property Forwarding

    • Forwards getters/setters and public fields directly to delegate.

Design Notes

1. Interceptable Return Types

The generator detects methods returning Future or FutureOr and wraps them in when<T>(), allowing asynchronous interception chains.

Future<User> getUser(String id) async => when<User>(
  () async => delegate.getUser(id),
  'getUser',
  MethodArguments(positionalArgs: [id]),
);

2. Aliased Imports

Every referenced type (return types, parameter types, supertypes) is imported via URI aliases to prevent symbol conflicts and to ensure generated proxies are always valid even across modular package boundaries.

import 'package:my_app/src/user_service.dart' as _i1;
import 'package:jetleaf_core/intercept.dart' as _i2;

3. Safety and Compatibility

  • Skips dart: internal URIs automatically (dart:_internal, etc.)
  • Skips final or external classes
  • Emits proxies into _jetleaf or configured build target directory

Internal API

_hasStereotypeAnnotation

Detects if a class has any of the known JetLeaf stereotypes, including meta-annotations (e.g., custom annotations that themselves are annotated with @Service).

_generateProxyForClass

The core emission logic that:

  • Writes imports
  • Declares proxy fields and constructors
  • Generates property accessors and method overrides
  • Implements toClass() from ClassGettable

_emitMethodOverride

Builds method overrides that wrap async methods in interceptable calls. Handles operator forwarding, parameter reconstruction, and argument serialization into MethodArguments.


Generated Output Example

final class $$OrderService with Interceptable implements OrderService, ClassGettable<OrderService> {
  final OrderService delegate;

  $$OrderService(this.delegate);

  @override
  Future<Order> findOrder(String id) async {
    return this.when<Order>(
      () async => delegate.findOrder(id),
      'findOrder',
      MethodArguments(positionalArgs: [id]),
    );
  }

  @override
  Class<OrderService> toClass() => Class<OrderService>(null, delegate.getClass().getPackage()?.getName());
}

Integration Points

System Role
Interceptable Provides when() logic and dispatcher bridge
ClassGettable Enables runtime type reflection support
ProxyCommandRunner CLI entrypoint that triggers ProxyGenerator via build_runner
MethodInterceptorDispatcher Executes registered interceptors per method

Developer Notes

  • Proxies are generated at compile-time via build_runner.
  • Proxy generation should be run prior to hot reload or packaging.
  • This generator does not modify the original source files.

  • Interceptable — mixin for runtime interception
  • ProxyCommandRunner — CLI command invoking this generator
  • MethodInterceptorDispatcher — dispatcher managing intercept chains
  • ClassGettable — runtime type metadata provider

Implementation

ProxyGenerator();