Skip to content

RestrictedBindingSource

Googler edited this page Aug 6, 2020 · 5 revisions

Restricting the Binding Source

Motivation

If you own a Guice library that provides a set of bindings, you may want to prevent code outside your library from providing them. Reasons include:

  • Preventing users from rolling their own fake implementation of your bindings in tests, in order to force them to use the canonical fake module your library maintains.

  • Letting alternate implementations of your set of bindings appear throughout your codebase makes it harder to refactor client code.

  • Guaranteeing a contract for downstream libraries. There can be invariants between your bindings that you want to guarantee by only allowing a limited set of vetted implementations - similar to how Guava guarantees the ImmutableSet contract by only allowing subclasses within the library.

The @RestrictedBindingSource annotation lets you do this - it lets you restrict which modules can provide your bindings. Annotating your bindings with @RestrictedBindingSource lets you specify a permit annotation that modules providing them need to be annotated with. For example, this is how you would ensure that @IpAddress Integer and RoutingTable bindings can only be provided by NetworkModule:

// Modules annotated with this Permit can provide Network bindings.
@RestrictedBindingSource.Permit
@Retention(RetentionPolicy.RUNTIME)
@interface NetworkPermit {}

// Bindings with the @IpAddress qualifier annotation can only be provided by
// modules with the NetworkPermit annotation.
@RestrictedBindingSource(
  explanation = "Please install NetworkModule instead of binding network bindings yourself.",
  permits = {NetworkPermit.class})
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface IpAddress {}

// The RoutingTable binding can only be provided by modules annotated with the
// NetworkPermit annotation.
@RestrictedBindingSource(
  explanation = "Please install NetworkModule instead of binding network bindings yourself.",
  permits = {NetworkPermit.class})
public interface RoutingTable {
  int getNextHopIpAddress(int destinationIpAddress);
}

@NetworkPermit
public final class NetworkModule extends AbstractModule {
  @Provides @IpAddress int provideIp( ... ) { ... }

  @Override
  protected void configure() {
    // RoutingModule is permitted to provide the RoutingTable binding because
    // its parent NetworkModule is annotated with NetworkPermit - ie. it's
    // enough for any module providing a binding (directly or indirectly) to
    // have the right permit.
    install(new RoutingModule());
  }
}

private final RoutingModule extends AbstractModule {
  @Provides RoutingTable provideRoutingTable( ... ) { ... }
}

ModuleAnnotatedMethodScanner as a Source of Bindings

In addition to modules, ModuleAnnotatedMethodScanners are also a source of bindings and can be annotated with permits to grant them permission to provide restricted bindings.

For example, if a scanner scans @FooProvides methods and binds types provided by those methods to @Foo Type, @Foo can be restricted such that only the scanner can provide bindings annotated with @Foo.

Clone this wiki locally

⌂ 换网址