com.google.inject.multibindings.OptionalBinder Maven / Gradle / Ivy
package com.google.inject.multibindings;
import static com.google.inject.internal.RealOptionalBinder.newRealOptionalBinder;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.RealOptionalBinder;
/**
* An API to bind optional values, optionally with a default value.
* OptionalBinder fulfills two roles:
* - It allows a framework to define an injection point that may or
* may not be bound by users.
*
- It allows a framework to supply a default value that can be changed
* by users.
*
*
* When an OptionalBinder is added, it will always supply the bindings: {@code Optional} and
* {@code Optional>}. Both {@link java.util.Optional java.util.Optional} and {@link
* com.google.common.base.Optional com.google.common.base.Optional} are bound for compatibility. If
* {@link #setBinding} or {@link #setDefault} are called, it will also bind {@code T}.
*
* {@code setDefault} is intended for use by frameworks that need a default
* value. User code can call {@code setBinding} to override the default.
* Warning: Even if setBinding is called, the default binding
* will still exist in the object graph. If it is a singleton, it will be
* instantiated in {@code Stage.PRODUCTION}.
*
*
If setDefault or setBinding are linked to Providers, the Provider may return
* {@code null}. If it does, the Optional bindings will be absent. Binding
* setBinding to a Provider that returns null will not cause OptionalBinder
* to fall back to the setDefault binding.
*
*
If neither setDefault nor setBinding are called, it will try to link to a
* user-supplied binding of the same type. If no binding exists, the optionals
* will be absent. Otherwise, if a user-supplied binding of that type exists,
* or if setBinding or setDefault are called, the optionals will return present
* if they are bound to a non-null value.
*
*
Values are resolved at injection time. If a value is bound to a
* provider, that provider's get method will be called each time the optional
* is injected (unless the binding is also scoped, or an optional of provider is
* injected).
*
*
Annotations are used to create different optionals of the same key/value
* type. Each distinct annotation gets its own independent binding.
*
*
* public class FrameworkModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Renamer.class);
* }
* }
*
* With this module, an {@code Optional} can now be
* injected. With no other bindings, the optional will be absent.
* Users can specify bindings in one of two ways:
*
* Option 1:
*
* public class UserRenamerModule extends AbstractModule {
* protected void configure() {
* bind(Renamer.class).to(ReplacingRenamer.class);
* }
* }
*
* or Option 2:
*
* public class UserRenamerModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Renamer.class)
* .setBinding().to(ReplacingRenamer.class);
* }
* }
* With both options, the {@code Optional} will be present and supply the
* ReplacingRenamer.
*
* Default values can be supplied using:
*
* public class FrameworkModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
* .setDefault().toInstance(DEFAULT_LOOKUP_URL);
* }
* }
* With the above module, code can inject an {@code @LookupUrl String} and it
* will supply the DEFAULT_LOOKUP_URL. A user can change this value by binding
*
* public class UserLookupModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
* .setBinding().toInstance(CUSTOM_LOOKUP_URL);
* }
* }
* ... which will override the default value.
*
* If one module uses setDefault the only way to override the default is to use setBinding.
* It is an error for a user to specify the binding without using OptionalBinder if
* setDefault or setBinding are called. For example,
*
* public class FrameworkModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
* .setDefault().toInstance(DEFAULT_LOOKUP_URL);
* }
* }
* public class UserLookupModule extends AbstractModule {
* protected void configure() {
* bind(Key.get(String.class, LookupUrl.class)).toInstance(CUSTOM_LOOKUP_URL);
* }
* }
* ... would generate an error, because both the framework and the user are trying to bind
* {@code @LookupUrl String}.
*/
public class OptionalBinder {
// This class is non-final due to users mocking this in tests :(
public static OptionalBinder newOptionalBinder(Binder binder, Class type) {
return new OptionalBinder<>(
newRealOptionalBinder(binder.skipSources(OptionalBinder.class), Key.get(type)));
}
public static OptionalBinder newOptionalBinder(Binder binder, TypeLiteral type) {
return new OptionalBinder<>(
newRealOptionalBinder(binder.skipSources(OptionalBinder.class), Key.get(type)));
}
public static OptionalBinder newOptionalBinder(Binder binder, Key type) {
return new OptionalBinder<>(
newRealOptionalBinder(binder.skipSources(OptionalBinder.class), type));
}
private final RealOptionalBinder delegate;
private OptionalBinder(RealOptionalBinder delegate) {
this.delegate = delegate;
}
/**
* Returns a binding builder used to set the default value that will be injected. The binding set
* by this method will be ignored if {@link #setBinding} is called.
*
* It is an error to call this method without also calling one of the {@code to} methods on the
* returned binding builder.
*/
public LinkedBindingBuilder setDefault() {
return delegate.setDefault();
}
/**
* Returns a binding builder used to set the actual value that will be injected. This overrides
* any binding set by {@link #setDefault}.
*
* It is an error to call this method without also calling one of the {@code to} methods on the
* returned binding builder.
*/
public LinkedBindingBuilder setBinding() {
return delegate.setBinding();
}
// Some tests depend on equals/hashCode behavior of OptionalBinder
@Override
public boolean equals(Object obj) {
if (obj instanceof OptionalBinder) {
return delegate.equals(((OptionalBinder>) obj).delegate);
}
return false;
}
@Override
public int hashCode() {
return delegate.hashCode();
}
}