Javadoc for PrivateModules
git-svn-id: https://google-guice.googlecode.com/svn/trunk@634 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/extensions/privatemodules/src/com/google/inject/privatemodules/PrivateModule.java b/extensions/privatemodules/src/com/google/inject/privatemodules/PrivateModule.java
index f469e41..c3082bc 100644
--- a/extensions/privatemodules/src/com/google/inject/privatemodules/PrivateModule.java
+++ b/extensions/privatemodules/src/com/google/inject/privatemodules/PrivateModule.java
@@ -51,6 +51,40 @@
import org.aopalliance.intercept.MethodInterceptor;
/**
+ * A module whose configuration information is hidden from other modules. Only bindings that are
+ * explicitly {@link #expose(Class) exposed} will be available to other modules and to the injector.
+ * Exposed keys must be explicitly bound, either directly or via another module that's installed
+ * by the private module.
+ *
+ * <p>In addition to the bindings configured via {@link #configurePrivateBindings()}, bindings will
+ * be created for all methods with the {@literal @}{@link com.google.inject.Provides Provides}
+ * annotation. These bindings will be hidden from other modules unless the methods also have the
+ * {@literal @}{@link Exposed} annotation:
+ *
+ * <pre>
+ * public class FooBarBazModule extends PrivateModule {
+ * protected void configurePrivateBindings() {
+ * bind(Foo.class).to(RealFoo.class);
+ * expose(Foo.class);
+ *
+ * install(new TransactionalBarModule());
+ * expose(Bar.class).annotatedWith(Transactional.class);
+ *
+ * bind(SomeImplementationDetail.class);
+ * install(new MoreImplementationDetailsModule());
+ * }
+ *
+ * {@literal @}Provides {@literal @}Exposed
+ * public Baz provideBaz() {
+ * return new SuperBaz();
+ * }
+ * }
+ * </pre>
+ *
+ * <p>Private modules are implemented with {@link Injector#createChildInjector(Module[]) parent
+ * injectors.} Types that inject an {@link Injector} will be provided with the child injector. This
+ * injector includes private bindings that are not available from the parent injector.
+ *
* @author jessewilson@google.com (Jesse Wilson)
*/
public abstract class PrivateModule implements Module {
@@ -68,32 +102,31 @@
private Binder privateBinder;
public final synchronized void configure(Binder binder) {
- // when exposes is null, we're being run for the public injector
+ // when 'exposes' is null, we're being run for the public injector
if (exposes == null) {
configurePublicBindings(binder);
+ return;
+ }
// otherwise we're being run for the private injector
- } else {
- checkState(this.privateBinder == null, "Re-entry is not allowed.");
- privateBinder = binder.skipSources(PrivateModule.class);
- try {
- configurePrivateBindings();
+ checkState(this.privateBinder == null, "Re-entry is not allowed.");
+ privateBinder = binder.skipSources(PrivateModule.class);
+ try {
+ configurePrivateBindings();
- ProviderMethodsModule providerMethodsModule = ProviderMethodsModule.forPrivateModule(this);
- for (ProviderMethod<?> providerMethod
- : providerMethodsModule.getProviderMethods(privateBinder)) {
- providerMethod.configure(privateBinder);
- if (providerMethod.getMethod().isAnnotationPresent(Exposed.class)) {
- expose(providerMethod.getKey());
- }
+ ProviderMethodsModule providerMethods = ProviderMethodsModule.forPrivateModule(this);
+ for (ProviderMethod<?> providerMethod : providerMethods.getProviderMethods(privateBinder)) {
+ providerMethod.configure(privateBinder);
+ if (providerMethod.getMethod().isAnnotationPresent(Exposed.class)) {
+ expose(providerMethod.getKey());
}
-
- for (Expose<?> expose : exposes) {
- expose.initPrivateProvider(binder);
- }
- } finally {
- privateBinder = null;
}
+
+ for (Expose<?> expose : exposes) {
+ expose.initPrivateProvider(binder);
+ }
+ } finally {
+ privateBinder = null;
}
}
@@ -131,15 +164,26 @@
}
}
+ /** Marker object used to indicate the private injector has been created */
private static class Ready {}
- public abstract void configurePrivateBindings();
+ /**
+ * Creates bindings and other configurations private to this module. Use {@link #expose(Class)
+ * expose()} to make the bindings in this module available externally.
+ */
+ protected abstract void configurePrivateBindings();
+ /** Makes the binding for {@code key} available to other modules and the injector. */
protected final <T> void expose(Key<T> key) {
checkState(exposes != null, "Cannot expose %s, private module is not ready");
exposes.add(new Expose<T>(sourceProvider.get(), readyProvider, key));
}
+ /**
+ * Makes a binding for {@code type} available to other modules and the injector. Use {@link
+ * ExposedKeyBuilder#annotatedWith(Class) annotatedWith()} to expose {@code type} with a binding
+ * annotation.
+ */
protected final <T> ExposedKeyBuilder expose(Class<T> type) {
checkState(exposes != null, "Cannot expose %s, private module is not ready");
Expose<T> expose = new Expose<T>(sourceProvider.get(), readyProvider, Key.get(type));
@@ -147,6 +191,11 @@
return expose;
}
+ /**
+ * Makes a binding for {@code type} available to other modules and the injector. Use {@link
+ * ExposedKeyBuilder#annotatedWith(Class) annotatedWith()} to expose {@code type} with a binding
+ * annotation.
+ */
protected final <T> ExposedKeyBuilder expose(TypeLiteral<T> type) {
checkState(exposes != null, "Cannot expose %s, private module is not ready");
Expose<T> expose = new Expose<T>(sourceProvider.get(), readyProvider, Key.get(type));
@@ -154,14 +203,13 @@
return expose;
}
+ /** Qualifies an exposed type with a binding annotation. */
public interface ExposedKeyBuilder {
void annotatedWith(Class<? extends Annotation> annotationType);
void annotatedWith(Annotation annotation);
}
- /**
- * A binding from the private injector exposed to the public injector.
- */
+ /** A binding from the private injector exposed to the public injector. */
private static class Expose<T> implements ExposedKeyBuilder, Provider<T> {
private final Object source;
private final Provider<Ready> readyProvider;
@@ -200,9 +248,7 @@
}
}
- /**
- * Returns the set of keys bound by {@code elements}.
- */
+ /** Returns the set of keys bound by {@code elements}. */
private Set<Key<?>> getBoundKeys(Iterable<? extends Element> elements) {
final Set<Key<?>> privatelyBoundKeys = Sets.newHashSet();
ElementVisitor<Void> visitor = new DefaultElementVisitor<Void>() {
diff --git a/src/com/google/inject/spi/ProviderMethods.java b/src/com/google/inject/spi/ProviderMethods.java
deleted file mode 100644
index 53506b0..0000000
--- a/src/com/google/inject/spi/ProviderMethods.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/**
- * Copyright (C) 2007 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.inject.spi;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import com.google.common.collect.Lists;
-import com.google.inject.Binder;
-import com.google.inject.Key;
-import com.google.inject.Module;
-import com.google.inject.Provider;
-import com.google.inject.Provides;
-import com.google.inject.TypeLiteral;
-import com.google.inject.binder.AnnotatedBindingBuilder;
-import com.google.inject.internal.Annotations;
-import com.google.inject.internal.Errors;
-import com.google.inject.internal.TypeResolver;
-import com.google.inject.util.Modules;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.util.List;
-
-/**
- * Creates bindings to methods annotated with {@literal @}{@link Provides}. Use the scope and
- * binding annotations on the provider method to configure the binding.
- */
-class ProviderMethods {
-
- /**
- * Returns a module which creates bindings for provider methods from the
- * given object.
- */
- static Module from(Object providers) {
- // avoid infinite recursion, since installing a module always installs itself
- if (providers instanceof ProviderMethodsModule) {
- return Modules.EMPTY_MODULE;
- }
-
- return new ProviderMethodsModule(providers);
- }
-
- static class ProviderMethodsModule implements Module {
- final Object providers;
- final TypeResolver typeResolver;
- Binder binder;
-
- ProviderMethodsModule(Object providers) {
- this.providers = checkNotNull(providers, "providers");
- this.typeResolver = new TypeResolver(providers.getClass());
- }
-
- public synchronized void configure(Binder binder) {
- checkState(this.binder == null, "Re-entry is not allowed.");
-
- for (Class c = providers.getClass(); c != Object.class; c = c.getSuperclass()) {
- for (Method method : c.getDeclaredMethods()) {
- if (!method.isAnnotationPresent(Provides.class)) {
- continue;
- }
-
- this.binder = binder.withSource(method);
- try {
- bindProviderMethod(method);
- } finally {
- this.binder = null;
- }
- }
- }
- }
-
- <T> void bindProviderMethod(final Method method) {
- Errors errors = new Errors(method);
-
- method.setAccessible(true);
-
- Class<? extends Annotation> scopeAnnotation
- = Annotations.findScopeAnnotation(errors, method.getAnnotations());
- Annotation bindingAnnotation
- = Annotations.findBindingAnnotation(errors, method, method.getAnnotations());
-
- final List<Provider<?>> parameterProviders = findParameterProviders(errors, method);
-
- for (Message message : errors.getMessages()) {
- binder.addError(message);
- }
-
- // Define T as the method's return type.
- @SuppressWarnings("unchecked") TypeLiteral<T> returnType
- = (TypeLiteral<T>) TypeLiteral.get(typeResolver.getReturnType(method));
-
- Provider<T> provider = new Provider<T>() {
- public T get() {
- Object[] parameters = new Object[parameterProviders.size()];
- for (int i = 0; i < parameters.length; i++) {
- parameters[i] = parameterProviders.get(i).get();
- }
-
- try {
- // We know this cast is safe becase T is the method's return type.
- @SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" })
- T result = (T) method.invoke(providers, parameters);
- return result;
- }
- catch (IllegalAccessException e) {
- throw new AssertionError(e);
- }
- catch (InvocationTargetException e) {
- throw new RuntimeException(e);
- }
- }
- };
-
- AnnotatedBindingBuilder<T> builder = binder.bind(returnType);
-
- if (bindingAnnotation != null) {
- builder.annotatedWith(bindingAnnotation);
- }
-
- builder.toProvider(provider);
-
- if (scopeAnnotation != null) {
- builder.in(scopeAnnotation);
- }
- }
-
- List<Provider<?>> findParameterProviders(Errors errors, Method method) {
- List<Provider<?>> parameterProviders = Lists.newArrayList();
-
- List<Type> parameterTypes = typeResolver.getParameterTypes(method);
- Annotation[][] parameterAnnotations = method.getParameterAnnotations();
- for (int i = 0; i < parameterTypes.size(); i++) {
- Type parameterType = parameterTypes.get(i);
- Annotation bindingAnnotation
- = Annotations.findBindingAnnotation(errors, method, parameterAnnotations[i]);
- Key<?> key = bindingAnnotation == null ? Key.get(parameterType)
- : Key.get(parameterType, bindingAnnotation);
- Provider<?> provider = binder.getProvider(key);
- parameterProviders.add(provider);
- }
-
- return parameterProviders;
- }
-
- @Override public boolean equals(Object o) {
- return o instanceof ProviderMethodsModule
- && ((ProviderMethodsModule) o).providers == providers;
- }
-
- @Override public int hashCode() {
- return providers.hashCode();
- }
- }
-}