Home | History | Annotate | Download | only in inject
      1 /**
      2  * Copyright (C) 2006 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject;
     18 
     19 import com.google.inject.internal.CircularDependencyProxy;
     20 import com.google.inject.internal.LinkedBindingImpl;
     21 import com.google.inject.internal.SingletonScope;
     22 import com.google.inject.spi.BindingScopingVisitor;
     23 import com.google.inject.spi.ExposedBinding;
     24 
     25 import java.lang.annotation.Annotation;
     26 
     27 /**
     28  * Built-in scope implementations.
     29  *
     30  * @author crazybob (at) google.com (Bob Lee)
     31  */
     32 public class Scopes {
     33 
     34   private Scopes() {}
     35 
     36   /**
     37    * One instance per {@link Injector}. Also see {@code @}{@link Singleton}.
     38    */
     39   public static final Scope SINGLETON = new SingletonScope();
     40 
     41   /**
     42    * No scope; the same as not applying any scope at all.  Each time the
     43    * Injector obtains an instance of an object with "no scope", it injects this
     44    * instance then immediately forgets it.  When the next request for the same
     45    * binding arrives it will need to obtain the instance over again.
     46    *
     47    * <p>This exists only in case a class has been annotated with a scope
     48    * annotation such as {@link Singleton @Singleton}, and you need to override
     49    * this to "no scope" in your binding.
     50    *
     51    * @since 2.0
     52    */
     53   public static final Scope NO_SCOPE = new Scope() {
     54     public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
     55       return unscoped;
     56     }
     57     @Override public String toString() {
     58       return "Scopes.NO_SCOPE";
     59     }
     60   };
     61 
     62   private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
     63       = new BindingScopingVisitor<Boolean>() {
     64         public Boolean visitNoScoping() {
     65           return false;
     66         }
     67 
     68         public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
     69           return scopeAnnotation == Singleton.class
     70               || scopeAnnotation == javax.inject.Singleton.class;
     71         }
     72 
     73         public Boolean visitScope(Scope scope) {
     74           return scope == Scopes.SINGLETON;
     75         }
     76 
     77         public Boolean visitEagerSingleton() {
     78           return true;
     79         }
     80       };
     81 
     82   /**
     83    * Returns true if {@code binding} is singleton-scoped. If the binding is a {@link
     84    * com.google.inject.spi.LinkedKeyBinding linked key binding} and belongs to an injector (ie. it
     85    * was retrieved via {@link Injector#getBinding Injector.getBinding()}), then this method will
     86    * also true if the target binding is singleton-scoped.
     87    *
     88    * @since 3.0
     89    */
     90   public static boolean isSingleton(Binding<?> binding) {
     91     do {
     92       boolean singleton = binding.acceptScopingVisitor(IS_SINGLETON_VISITOR);
     93       if (singleton) {
     94         return true;
     95       }
     96 
     97       if (binding instanceof LinkedBindingImpl) {
     98         LinkedBindingImpl<?> linkedBinding = (LinkedBindingImpl) binding;
     99         Injector injector = linkedBinding.getInjector();
    100         if (injector != null) {
    101           binding = injector.getBinding(linkedBinding.getLinkedKey());
    102           continue;
    103         }
    104       } else if(binding instanceof ExposedBinding) {
    105         ExposedBinding<?> exposedBinding = (ExposedBinding)binding;
    106         Injector injector = exposedBinding.getPrivateElements().getInjector();
    107         if (injector != null) {
    108           binding = injector.getBinding(exposedBinding.getKey());
    109           continue;
    110         }
    111       }
    112 
    113       return false;
    114     } while (true);
    115   }
    116 
    117   /**
    118 
    119    * Returns true if {@code binding} has the given scope. If the binding is a {@link
    120    * com.google.inject.spi.LinkedKeyBinding linked key binding} and belongs to an injector (ie. it
    121    * was retrieved via {@link Injector#getBinding Injector.getBinding()}), then this method will
    122    * also true if the target binding has the given scope.
    123    *
    124    * @param binding binding to check
    125    * @param scope scope implementation instance
    126    * @param scopeAnnotation scope annotation class
    127    * @since 4.0
    128    */
    129   public static boolean isScoped(Binding<?> binding, final Scope scope,
    130       final Class<? extends Annotation> scopeAnnotation) {
    131     do {
    132       boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
    133         public Boolean visitNoScoping() {
    134           return false;
    135         }
    136 
    137         public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
    138           return visitedAnnotation == scopeAnnotation;
    139         }
    140 
    141         public Boolean visitScope(Scope visitedScope) {
    142           return visitedScope == scope;
    143         }
    144 
    145         public Boolean visitEagerSingleton() {
    146           return false;
    147         }
    148       });
    149 
    150       if (matches) {
    151         return true;
    152       }
    153 
    154       if (binding instanceof LinkedBindingImpl) {
    155         LinkedBindingImpl<?> linkedBinding = (LinkedBindingImpl) binding;
    156         Injector injector = linkedBinding.getInjector();
    157         if (injector != null) {
    158           binding = injector.getBinding(linkedBinding.getLinkedKey());
    159           continue;
    160         }
    161       } else if(binding instanceof ExposedBinding) {
    162         ExposedBinding<?> exposedBinding = (ExposedBinding)binding;
    163         Injector injector = exposedBinding.getPrivateElements().getInjector();
    164         if (injector != null) {
    165           binding = injector.getBinding(exposedBinding.getKey());
    166           continue;
    167         }
    168       }
    169 
    170       return false;
    171     } while (true);
    172   }
    173 
    174   /**
    175    * Returns true if the object is a proxy for a circular dependency,
    176    * constructed by Guice because it encountered a circular dependency. Scope
    177    * implementations should be careful to <b>not cache circular proxies</b>,
    178    * because the proxies are not intended for general purpose use. (They are
    179    * designed just to fulfill the immediate injection, not all injections.
    180    * Caching them can lead to IllegalArgumentExceptions or ClassCastExceptions.)
    181    *
    182    * @since 4.0
    183    */
    184   public static boolean isCircularProxy(Object object) {
    185     return object instanceof CircularDependencyProxy;
    186   }
    187 }
    188