All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.inject.internal.InternalInjectorCreator Maven / Gradle / Ivy

There is a newer version: 3.0.0-alpha-3
Show newest version
/*
 * 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.internal;

import com.google.common.base.Stopwatch;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.ContinuousStopwatch;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.TypeConverterBinding;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Builds a tree of injectors. This is a primary injector, plus child injectors needed for each
 * {@code Binder.newPrivateBinder() private environment}. The primary injector is not necessarily a
 * top-level injector.
 *
 * 

Injector construction happens in two phases. * *

    *
  1. Static building. In this phase, we interpret commands, create bindings, and inspect * dependencies. During this phase, we hold a lock to ensure consistency with parent * injectors. No user code is executed in this phase. *
  2. Dynamic injection. In this phase, we call user code. We inject members that requested * injection. This may require user's objects be created and their providers be called. And we * create eager singletons. In this phase, user code may have started other threads. This * phase is not executed for injectors created using {@link Stage#TOOL the tool stage} *
* * @author [email protected] (Bob Lee) * @author [email protected] (Jesse Wilson) */ public final class InternalInjectorCreator { private final ContinuousStopwatch stopwatch = new ContinuousStopwatch(Stopwatch.createUnstarted()); private final Errors errors = new Errors(); private final Initializer initializer = new Initializer(); private final ProcessedBindingData processedBindingData; private final InjectionRequestProcessor injectionRequestProcessor; private final InjectorShell.Builder shellBuilder = new InjectorShell.Builder(); private List shells; public InternalInjectorCreator() { injectionRequestProcessor = new InjectionRequestProcessor(errors, initializer); processedBindingData = new ProcessedBindingData(); } public InternalInjectorCreator stage(Stage stage) { shellBuilder.stage(stage); return this; } /** * Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's * stage to the stage of {@code parent} and sets {@link #requireExplicitBindings()} if the parent * injector also required them. */ public InternalInjectorCreator parentInjector(InjectorImpl parent) { shellBuilder.parent(parent); return this; } public InternalInjectorCreator addModules(Iterable modules) { shellBuilder.addModules(modules); return this; } public Injector build() { if (shellBuilder == null) { throw new AssertionError("Already built, builders are not reusable."); } // Synchronize while we're building up the bindings and other injector data. This ensures that // the JIT bindings in the parent injector don't change while we're being built synchronized (shellBuilder.lock()) { shells = shellBuilder.build(initializer, processedBindingData, stopwatch, errors); stopwatch.resetAndLog("Injector construction"); initializeStatically(); } injectDynamically(); if (shellBuilder.getStage() == Stage.TOOL) { // wrap the primaryInjector in a ToolStageInjector // to prevent non-tool-friendy methods from being called. return new ToolStageInjector(primaryInjector()); } else { return primaryInjector(); } } /** Initialize and validate everything. */ private void initializeStatically() { processedBindingData.initializeBindings(); stopwatch.resetAndLog("Binding initialization"); for (InjectorShell shell : shells) { shell.getInjector().getBindingData().indexBindingsByType(); } stopwatch.resetAndLog("Binding indexing"); injectionRequestProcessor.process(shells); stopwatch.resetAndLog("Collecting injection requests"); processedBindingData.runCreationListeners(errors); stopwatch.resetAndLog("Binding validation"); injectionRequestProcessor.validate(); stopwatch.resetAndLog("Static validation"); initializer.validateOustandingInjections(errors); stopwatch.resetAndLog("Instance member validation"); new LookupProcessor(errors).process(shells); for (InjectorShell shell : shells) { ((DeferredLookups) shell.getInjector().lookups).initialize(errors); } stopwatch.resetAndLog("Provider verification"); // This needs to come late since some user bindings rely on requireBinding calls to create // jit bindings during the LookupProcessor. processedBindingData.initializeDelayedBindings(); stopwatch.resetAndLog("Delayed Binding initialization"); for (InjectorShell shell : shells) { if (!shell.getElements().isEmpty()) { throw new AssertionError("Failed to execute " + shell.getElements()); } } errors.throwCreationExceptionIfErrorsExist(); } /** Returns the injector being constructed. This is not necessarily the root injector. */ private Injector primaryInjector() { return shells.get(0).getInjector(); } /** * Inject everything that can be injected. This method is intentionally not synchronized. If we * locked while injecting members (ie. running user code), things would deadlock should the user * code build a just-in-time binding from another thread. */ private void injectDynamically() { injectionRequestProcessor.injectMembers(); stopwatch.resetAndLog("Static member injection"); initializer.injectAll(errors); stopwatch.resetAndLog("Instance injection"); errors.throwCreationExceptionIfErrorsExist(); if (shellBuilder.getStage() != Stage.TOOL) { for (InjectorShell shell : shells) { loadEagerSingletons(shell.getInjector(), shellBuilder.getStage(), errors); } stopwatch.resetAndLog("Preloading singletons"); } errors.throwCreationExceptionIfErrorsExist(); } /** * Loads eager singletons, or all singletons if we're in Stage.PRODUCTION. Bindings discovered * while we're binding these singletons are not be eager. */ void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) { List> candidateBindings = new ArrayList<>(); @SuppressWarnings("unchecked") // casting Collection to Collection is safe Collection> bindingsAtThisLevel = (Collection) injector.getBindingData().getExplicitBindingsThisLevel().values(); candidateBindings.addAll(bindingsAtThisLevel); synchronized (injector.getJitBindingData().lock()) { // jit bindings must be accessed while holding the lock. candidateBindings.addAll(injector.getJitBindingData().getJitBindings().values()); } InternalContext context = injector.enterContext(); try { for (BindingImpl binding : candidateBindings) { if (isEagerSingleton(injector, binding, stage)) { Dependency dependency = Dependency.get(binding.getKey()); try { binding.getInternalFactory().get(context, dependency, false); } catch (InternalProvisionException e) { errors.withSource(dependency).merge(e); } } } } finally { context.close(); } } private boolean isEagerSingleton(InjectorImpl injector, BindingImpl binding, Stage stage) { if (binding.getScoping().isEagerSingleton(stage)) { return true; } // handle a corner case where a child injector links to a binding in a parent injector, and // that binding is singleton. We won't catch this otherwise because we only iterate the child's // bindings. This only applies if the linked binding is not itself scoped. if (binding instanceof LinkedBindingImpl) { Key linkedBinding = ((LinkedBindingImpl) binding).getLinkedKey(); return binding.getScoping().isNoScope() && isEagerSingleton(injector, injector.getBinding(linkedBinding), stage); } return false; } /** {@link Injector} exposed to users in {@link Stage#TOOL}. */ static class ToolStageInjector implements Injector { private final Injector delegateInjector; ToolStageInjector(Injector delegateInjector) { this.delegateInjector = delegateInjector; } @Override public void injectMembers(Object o) { throw new UnsupportedOperationException( "Injector.injectMembers(Object) is not supported in Stage.TOOL"); } @Override public Map, Binding> getBindings() { return this.delegateInjector.getBindings(); } @Override public Map, Binding> getAllBindings() { return this.delegateInjector.getAllBindings(); } @Override public Binding getBinding(Key key) { return this.delegateInjector.getBinding(key); } @Override public Binding getBinding(Class type) { return this.delegateInjector.getBinding(type); } @Override public Binding getExistingBinding(Key key) { return this.delegateInjector.getExistingBinding(key); } @Override public List> findBindingsByType(TypeLiteral type) { return this.delegateInjector.findBindingsByType(type); } @Override public Injector getParent() { return delegateInjector.getParent(); } @Override public Injector createChildInjector(Iterable modules) { return delegateInjector.createChildInjector(modules); } @Override public Injector createChildInjector(Module... modules) { return delegateInjector.createChildInjector(modules); } @Override public Map, Scope> getScopeBindings() { return delegateInjector.getScopeBindings(); } @Override public Set getTypeConverterBindings() { return delegateInjector.getTypeConverterBindings(); } @Override public List getElements() { return delegateInjector.getElements(); } @Override public Map, List> getAllMembersInjectorInjectionPoints() { return delegateInjector.getAllMembersInjectorInjectionPoints(); } @Override public Provider getProvider(Key key) { throw new UnsupportedOperationException( "Injector.getProvider(Key) is not supported in Stage.TOOL"); } @Override public Provider getProvider(Class type) { throw new UnsupportedOperationException( "Injector.getProvider(Class) is not supported in Stage.TOOL"); } @Override public MembersInjector getMembersInjector(TypeLiteral typeLiteral) { throw new UnsupportedOperationException( "Injector.getMembersInjector(TypeLiteral) is not supported in Stage.TOOL"); } @Override public MembersInjector getMembersInjector(Class type) { throw new UnsupportedOperationException( "Injector.getMembersInjector(Class) is not supported in Stage.TOOL"); } @Override public T getInstance(Key key) { throw new UnsupportedOperationException( "Injector.getInstance(Key) is not supported in Stage.TOOL"); } @Override public T getInstance(Class type) { throw new UnsupportedOperationException( "Injector.getInstance(Class) is not supported in Stage.TOOL"); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy