| /** |
| * Copyright (C) 2006 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; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| import static com.google.inject.Scopes.SINGLETON; |
| import com.google.inject.internal.Errors; |
| import com.google.inject.internal.ErrorsException; |
| import com.google.inject.internal.SourceProvider; |
| import com.google.inject.internal.Stopwatch; |
| import com.google.inject.spi.Dependency; |
| import com.google.inject.spi.Element; |
| import com.google.inject.spi.Elements; |
| import com.google.inject.spi.InjectionPoint; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.logging.Logger; |
| |
| /** |
| * Builds a dependency injection {@link Injector}. |
| * |
| * @author crazybob@google.com (Bob Lee) |
| * @author jessewilson@google.com (Jesse Wilson) |
| */ |
| class InjectorBuilder { |
| |
| private final Stopwatch stopwatch = new Stopwatch(); |
| |
| private InjectorImpl parent = null; |
| private Stage stage; |
| private final List<Module> modules = Lists.newLinkedList(); |
| |
| private InjectorImpl injector; |
| private Errors errors = new Errors(); |
| |
| private final List<Element> elements = Lists.newArrayList(); |
| |
| private BindingProcessor bindCommandProcesor; |
| private InjectionRequestProcessor injectionCommandProcessor; |
| |
| /** |
| * @param stage we're running in. If the stage is {@link Stage#PRODUCTION}, we will eagerly load |
| * singletons. |
| */ |
| InjectorBuilder stage(Stage stage) { |
| this.stage = stage; |
| return this; |
| } |
| |
| InjectorBuilder parentInjector(InjectorImpl parent) { |
| this.parent = parent; |
| return this; |
| } |
| |
| InjectorBuilder addModules(Iterable<? extends Module> modules) { |
| for (Module module : modules) { |
| this.modules.add(module); |
| } |
| return this; |
| } |
| |
| Injector build() { |
| if (injector != null) { |
| throw new AssertionError("Already built, builders are not reusable."); |
| } |
| |
| injector = new InjectorImpl(parent); |
| |
| // bind Stage and Singleton if this is a top-level injector |
| if (parent == null) { |
| modules.add(0, new RootModule(stage)); |
| } |
| |
| elements.addAll(Elements.getElements(stage, modules)); |
| |
| buildCoreInjector(); |
| |
| validate(); |
| |
| errors.throwCreationExceptionIfErrorsExist(); |
| |
| // If we're in the tool stage, stop here. Don't eagerly inject or load |
| // anything. |
| if (stage == Stage.TOOL) { |
| return new ToolStageInjector(injector); |
| } |
| |
| fulfillInjectionRequests(); |
| |
| if (!elements.isEmpty()) { |
| throw new AssertionError("Failed to execute " + elements); |
| } |
| |
| return injector; |
| } |
| |
| /** Builds the injector. */ |
| private void buildCoreInjector() { |
| new MessageProcessor(errors) |
| .processCommands(elements); |
| |
| InterceptorBindingProcessor interceptorCommandProcessor |
| = new InterceptorBindingProcessor(errors, injector.state); |
| interceptorCommandProcessor.processCommands(elements); |
| injector.constructionProxyFactory = interceptorCommandProcessor.createProxyFactory(); |
| stopwatch.resetAndLog("Interceptors creation"); |
| |
| new ScopeBindingProcessor(errors, injector.state).processCommands(elements); |
| stopwatch.resetAndLog("Scopes creation"); |
| |
| new TypeConverterBindingProcessor(errors, injector.state).processCommands(elements); |
| stopwatch.resetAndLog("Converters creation"); |
| |
| bindInjector(); |
| bindLogger(); |
| bindCommandProcesor = new BindingProcessor(errors, |
| injector, injector.state, injector.initializer); |
| bindCommandProcesor.processCommands(elements); |
| bindCommandProcesor.createUntargettedBindings(); |
| stopwatch.resetAndLog("Binding creation"); |
| |
| injector.index(); |
| stopwatch.resetAndLog("Binding indexing"); |
| |
| injectionCommandProcessor = new InjectionRequestProcessor(errors, injector.initializer); |
| injectionCommandProcessor.processCommands(elements); |
| stopwatch.resetAndLog("Static injection"); |
| } |
| |
| /** Validate everything that we can validate now that the injector is ready for use. */ |
| private void validate() { |
| bindCommandProcesor.runCreationListeners(injector); |
| stopwatch.resetAndLog("Validation"); |
| |
| injectionCommandProcessor.validate(injector); |
| stopwatch.resetAndLog("Static validation"); |
| |
| injector.initializer.validateOustandingInjections(errors); |
| stopwatch.resetAndLog("Instance member validation"); |
| |
| new ProviderLookupProcessor(errors, injector).processCommands(elements); |
| stopwatch.resetAndLog("Provider verification"); |
| |
| errors.throwCreationExceptionIfErrorsExist(); |
| } |
| |
| /** Inject everything that can be injected. */ |
| private void fulfillInjectionRequests() { |
| injectionCommandProcessor.injectMembers(injector); |
| stopwatch.resetAndLog("Static member injection"); |
| |
| injector.initializer.injectAll(errors); |
| stopwatch.resetAndLog("Instance injection"); |
| errors.throwCreationExceptionIfErrorsExist(); |
| |
| loadEagerSingletons(); |
| stopwatch.resetAndLog("Preloading"); |
| errors.throwCreationExceptionIfErrorsExist(); |
| } |
| |
| public void loadEagerSingletons() { |
| // load eager singletons, or all singletons if we're in Stage.PRODUCTION. |
| // Bindings discovered while we're binding these singletons are not be eager. |
| @SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe |
| Set<BindingImpl<?>> candidateBindings = ImmutableSet.copyOf(Iterables.concat( |
| (Collection) injector.state.getExplicitBindingsThisLevel().values(), |
| injector.jitBindings.values())); |
| for (final BindingImpl<?> binding : candidateBindings) { |
| if ((stage == Stage.PRODUCTION && binding.getScope() == SINGLETON) |
| || binding.getLoadStrategy() == LoadStrategy.EAGER) { |
| try { |
| injector.callInContext(new ContextualCallable<Void>() { |
| Dependency<?> dependency = Dependency.get(binding.key); |
| public Void call(InternalContext context) { |
| context.setDependency(dependency); |
| Errors errorsForBinding = errors.withSource(dependency); |
| try { |
| binding.internalFactory.get(errorsForBinding, context, dependency); |
| } catch (ErrorsException e) { |
| errorsForBinding.merge(e.getErrors()); |
| } finally { |
| context.setDependency(null); |
| } |
| |
| return null; |
| } |
| }); |
| } catch (ErrorsException e) { |
| throw new AssertionError(); |
| } |
| } |
| } |
| } |
| |
| private static class RootModule implements Module { |
| final Stage stage; |
| |
| private RootModule(Stage stage) { |
| this.stage = checkNotNull(stage, "stage"); |
| } |
| |
| public void configure(Binder binder) { |
| binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE); |
| binder.bind(Stage.class).toInstance(stage); |
| binder.bindScope(Singleton.class, SINGLETON); |
| } |
| } |
| |
| /** |
| * The Injector is a special case because we allow both parent and child injectors to both have |
| * a binding for that key. |
| */ |
| private void bindInjector() { |
| Key<Injector> key = Key.get(Injector.class); |
| InjectorFactory injectorFactory = new InjectorFactory(injector); |
| injector.state.putBinding(key, |
| new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE, |
| injectorFactory, Scopes.NO_SCOPE, injectorFactory, LoadStrategy.LAZY, |
| ImmutableSet.<InjectionPoint>of())); |
| } |
| |
| static class InjectorFactory implements InternalFactory<Injector>, Provider<Injector> { |
| private final Injector injector; |
| |
| private InjectorFactory(Injector injector) { |
| this.injector = injector; |
| } |
| |
| public Injector get(Errors errors, InternalContext context, Dependency<?> dependency) |
| throws ErrorsException { |
| return injector; |
| } |
| |
| public Injector get() { |
| return injector; |
| } |
| |
| public String toString() { |
| return "Provider<Injector>"; |
| } |
| } |
| |
| /** |
| * The Logger is a special case because it knows the injection point of the injected member. It's |
| * the only binding that does this. |
| */ |
| private void bindLogger() { |
| Key<Logger> key = Key.get(Logger.class); |
| LoggerFactory loggerFactory = new LoggerFactory(); |
| injector.state.putBinding(key, |
| new ProviderInstanceBindingImpl<Logger>(injector, key, |
| SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE, |
| loggerFactory, LoadStrategy.LAZY, ImmutableSet.<InjectionPoint>of())); |
| } |
| |
| static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> { |
| public Logger get(Errors errors, InternalContext context, Dependency<?> dependency) { |
| InjectionPoint injectionPoint = dependency.getInjectionPoint(); |
| return injectionPoint == null |
| ? Logger.getAnonymousLogger() |
| : Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); |
| } |
| |
| public Logger get() { |
| return Logger.getAnonymousLogger(); |
| } |
| |
| public String toString() { |
| return "Provider<Logger>"; |
| } |
| } |
| |
| /** {@link Injector} exposed to users in {@link Stage#TOOL}. */ |
| static class ToolStageInjector implements Injector { |
| private final Injector delegateInjector; |
| |
| ToolStageInjector(Injector delegateInjector) { |
| this.delegateInjector = delegateInjector; |
| } |
| public void injectMembers(Object o) { |
| throw new UnsupportedOperationException( |
| "Injector.injectMembers(Object) is not supported in Stage.TOOL"); |
| } |
| public Map<Key<?>, Binding<?>> getBindings() { |
| return this.delegateInjector.getBindings(); |
| } |
| public <T> Binding<T> getBinding(Key<T> key) { |
| return this.delegateInjector.getBinding(key); |
| } |
| public <T> Binding<T> getBinding(Class<T> type) { |
| return this.delegateInjector.getBinding(type); |
| } |
| public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) { |
| return this.delegateInjector.findBindingsByType(type); |
| } |
| public Injector getParent() { |
| return delegateInjector.getParent(); |
| } |
| public Injector createChildInjector(Iterable<? extends Module> modules) { |
| return delegateInjector.createChildInjector(modules); |
| } |
| public Injector createChildInjector(Module... modules) { |
| return delegateInjector.createChildInjector(modules); |
| } |
| public <T> Provider<T> getProvider(Key<T> key) { |
| throw new UnsupportedOperationException( |
| "Injector.getProvider(Key<T>) is not supported in Stage.TOOL"); |
| } |
| public <T> Provider<T> getProvider(Class<T> type) { |
| throw new UnsupportedOperationException( |
| "Injector.getProvider(Class<T>) is not supported in Stage.TOOL"); |
| } |
| public <T> T getInstance(Key<T> key) { |
| throw new UnsupportedOperationException( |
| "Injector.getInstance(Key<T>) is not supported in Stage.TOOL"); |
| } |
| public <T> T getInstance(Class<T> type) { |
| throw new UnsupportedOperationException( |
| "Injector.getInstance(Class<T>) is not supported in Stage.TOOL"); |
| } |
| } |
| } |