1 /* 2 * Copyright (C) 2007 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.internal; 18 19 import static com.google.common.base.Preconditions.checkState; 20 import static com.google.inject.internal.Annotations.findScopeAnnotation; 21 22 import com.google.common.base.MoreObjects; 23 import com.google.common.base.Objects; 24 import com.google.common.collect.ImmutableSet; 25 import com.google.inject.Binder; 26 import com.google.inject.ConfigurationException; 27 import com.google.inject.Inject; 28 import com.google.inject.Key; 29 import com.google.inject.TypeLiteral; 30 import com.google.inject.internal.util.Classes; 31 import com.google.inject.spi.BindingTargetVisitor; 32 import com.google.inject.spi.ConstructorBinding; 33 import com.google.inject.spi.Dependency; 34 import com.google.inject.spi.InjectionPoint; 35 import java.lang.annotation.Annotation; 36 import java.lang.reflect.Constructor; 37 import java.lang.reflect.Method; 38 import java.lang.reflect.Modifier; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Set; 42 43 final class ConstructorBindingImpl<T> extends BindingImpl<T> 44 implements ConstructorBinding<T>, DelayedInitialize { 45 46 private final Factory<T> factory; 47 private final InjectionPoint constructorInjectionPoint; 48 ConstructorBindingImpl( InjectorImpl injector, Key<T> key, Object source, InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory, InjectionPoint constructorInjectionPoint)49 private ConstructorBindingImpl( 50 InjectorImpl injector, 51 Key<T> key, 52 Object source, 53 InternalFactory<? extends T> scopedFactory, 54 Scoping scoping, 55 Factory<T> factory, 56 InjectionPoint constructorInjectionPoint) { 57 super(injector, key, source, scopedFactory, scoping); 58 this.factory = factory; 59 this.constructorInjectionPoint = constructorInjectionPoint; 60 } 61 ConstructorBindingImpl( Key<T> key, Object source, Scoping scoping, InjectionPoint constructorInjectionPoint, Set<InjectionPoint> injectionPoints)62 public ConstructorBindingImpl( 63 Key<T> key, 64 Object source, 65 Scoping scoping, 66 InjectionPoint constructorInjectionPoint, 67 Set<InjectionPoint> injectionPoints) { 68 super(source, key, scoping); 69 this.factory = new Factory<>(false, key); 70 ConstructionProxy<T> constructionProxy = 71 new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create(); 72 this.constructorInjectionPoint = constructorInjectionPoint; 73 factory.constructorInjector = 74 new ConstructorInjector<T>(injectionPoints, constructionProxy, null, null); 75 } 76 77 /** 78 * @param constructorInjector the constructor to use, or {@code null} to use the default. 79 * @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should only 80 * succeed if retrieved from a linked binding 81 */ create( InjectorImpl injector, Key<T> key, InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors, boolean failIfNotLinked, boolean failIfNotExplicit)82 static <T> ConstructorBindingImpl<T> create( 83 InjectorImpl injector, 84 Key<T> key, 85 InjectionPoint constructorInjector, 86 Object source, 87 Scoping scoping, 88 Errors errors, 89 boolean failIfNotLinked, 90 boolean failIfNotExplicit) 91 throws ErrorsException { 92 int numErrors = errors.size(); 93 94 @SuppressWarnings("unchecked") // constructorBinding guarantees type is consistent 95 Class<? super T> rawType = 96 constructorInjector == null 97 ? key.getTypeLiteral().getRawType() 98 : (Class) constructorInjector.getDeclaringType().getRawType(); 99 100 // We can't inject abstract classes. 101 if (Modifier.isAbstract(rawType.getModifiers())) { 102 errors.missingImplementationWithHint(key, injector); 103 } 104 105 // Error: Inner class. 106 if (Classes.isInnerClass(rawType)) { 107 errors.cannotInjectInnerClass(rawType); 108 } 109 110 errors.throwIfNewErrors(numErrors); 111 112 // Find a constructor annotated @Inject 113 if (constructorInjector == null) { 114 try { 115 constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral()); 116 if (failIfNotExplicit && !hasAtInject((Constructor) constructorInjector.getMember())) { 117 errors.atInjectRequired(rawType); 118 } 119 } catch (ConfigurationException e) { 120 throw errors.merge(e.getErrorMessages()).toException(); 121 } 122 } 123 124 // if no scope is specified, look for a scoping annotation on the concrete class 125 if (!scoping.isExplicitlyScoped()) { 126 Class<?> annotatedType = constructorInjector.getMember().getDeclaringClass(); 127 Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, annotatedType); 128 if (scopeAnnotation != null) { 129 scoping = 130 Scoping.makeInjectable( 131 Scoping.forAnnotation(scopeAnnotation), injector, errors.withSource(rawType)); 132 } 133 } 134 135 errors.throwIfNewErrors(numErrors); 136 137 Factory<T> factoryFactory = new Factory<>(failIfNotLinked, key); 138 InternalFactory<? extends T> scopedFactory = 139 Scoping.scope(key, injector, factoryFactory, source, scoping); 140 141 return new ConstructorBindingImpl<T>( 142 injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector); 143 } 144 145 /** Returns true if the inject annotation is on the constructor. */ hasAtInject(Constructor cxtor)146 private static boolean hasAtInject(Constructor cxtor) { 147 return cxtor.isAnnotationPresent(Inject.class) 148 || cxtor.isAnnotationPresent(javax.inject.Inject.class); 149 } 150 151 @Override 152 @SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type initialize(InjectorImpl injector, Errors errors)153 public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException { 154 factory.constructorInjector = 155 (ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors); 156 factory.provisionCallback = injector.provisionListenerStore.get(this); 157 } 158 159 /** True if this binding has been initialized and is ready for use. */ isInitialized()160 boolean isInitialized() { 161 return factory.constructorInjector != null; 162 } 163 164 /** Returns an injection point that can be used to clean up the constructor store. */ getInternalConstructor()165 InjectionPoint getInternalConstructor() { 166 if (factory.constructorInjector != null) { 167 return factory.constructorInjector.getConstructionProxy().getInjectionPoint(); 168 } else { 169 return constructorInjectionPoint; 170 } 171 } 172 173 /** Returns a set of dependencies that can be iterated over to clean up stray JIT bindings. */ getInternalDependencies()174 Set<Dependency<?>> getInternalDependencies() { 175 ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder(); 176 if (factory.constructorInjector == null) { 177 builder.add(constructorInjectionPoint); 178 // If the below throws, it's OK -- we just ignore those dependencies, because no one 179 // could have used them anyway. 180 try { 181 builder.addAll( 182 InjectionPoint.forInstanceMethodsAndFields( 183 constructorInjectionPoint.getDeclaringType())); 184 } catch (ConfigurationException ignored) { 185 } 186 } else { 187 builder.add(getConstructor()).addAll(getInjectableMembers()); 188 } 189 190 return Dependency.forInjectionPoints(builder.build()); 191 } 192 193 @Override acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor)194 public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { 195 checkState(factory.constructorInjector != null, "not initialized"); 196 return visitor.visit(this); 197 } 198 199 @Override getConstructor()200 public InjectionPoint getConstructor() { 201 checkState(factory.constructorInjector != null, "Binding is not ready"); 202 return factory.constructorInjector.getConstructionProxy().getInjectionPoint(); 203 } 204 205 @Override getInjectableMembers()206 public Set<InjectionPoint> getInjectableMembers() { 207 checkState(factory.constructorInjector != null, "Binding is not ready"); 208 return factory.constructorInjector.getInjectableMembers(); 209 } 210 211 /*if[AOP]*/ 212 @Override getMethodInterceptors()213 public Map<Method, List<org.aopalliance.intercept.MethodInterceptor>> getMethodInterceptors() { 214 checkState(factory.constructorInjector != null, "Binding is not ready"); 215 return factory.constructorInjector.getConstructionProxy().getMethodInterceptors(); 216 } 217 /*end[AOP]*/ 218 219 @Override getDependencies()220 public Set<Dependency<?>> getDependencies() { 221 return Dependency.forInjectionPoints( 222 new ImmutableSet.Builder<InjectionPoint>() 223 .add(getConstructor()) 224 .addAll(getInjectableMembers()) 225 .build()); 226 } 227 228 @Override withScoping(Scoping scoping)229 protected BindingImpl<T> withScoping(Scoping scoping) { 230 return new ConstructorBindingImpl<T>( 231 null, getKey(), getSource(), factory, scoping, factory, constructorInjectionPoint); 232 } 233 234 @Override withKey(Key<T> key)235 protected BindingImpl<T> withKey(Key<T> key) { 236 return new ConstructorBindingImpl<T>( 237 null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint); 238 } 239 240 @Override 241 @SuppressWarnings("unchecked") // the raw constructor member and declaring type always agree applyTo(Binder binder)242 public void applyTo(Binder binder) { 243 InjectionPoint constructor = getConstructor(); 244 getScoping() 245 .applyTo( 246 binder 247 .withSource(getSource()) 248 .bind(getKey()) 249 .toConstructor( 250 (Constructor) getConstructor().getMember(), 251 (TypeLiteral) constructor.getDeclaringType())); 252 } 253 254 @Override toString()255 public String toString() { 256 return MoreObjects.toStringHelper(ConstructorBinding.class) 257 .add("key", getKey()) 258 .add("source", getSource()) 259 .add("scope", getScoping()) 260 .toString(); 261 } 262 263 @Override equals(Object obj)264 public boolean equals(Object obj) { 265 if (obj instanceof ConstructorBindingImpl) { 266 ConstructorBindingImpl<?> o = (ConstructorBindingImpl<?>) obj; 267 return getKey().equals(o.getKey()) 268 && getScoping().equals(o.getScoping()) 269 && Objects.equal(constructorInjectionPoint, o.constructorInjectionPoint); 270 } else { 271 return false; 272 } 273 } 274 275 @Override hashCode()276 public int hashCode() { 277 return Objects.hashCode(getKey(), getScoping(), constructorInjectionPoint); 278 } 279 280 private static class Factory<T> implements InternalFactory<T> { 281 private final boolean failIfNotLinked; 282 private final Key<?> key; 283 private ConstructorInjector<T> constructorInjector; 284 private ProvisionListenerStackCallback<T> provisionCallback; 285 Factory(boolean failIfNotLinked, Key<?> key)286 Factory(boolean failIfNotLinked, Key<?> key) { 287 this.failIfNotLinked = failIfNotLinked; 288 this.key = key; 289 } 290 291 @Override 292 @SuppressWarnings("unchecked") get(InternalContext context, Dependency<?> dependency, boolean linked)293 public T get(InternalContext context, Dependency<?> dependency, boolean linked) 294 throws InternalProvisionException { 295 ConstructorInjector<T> localInjector = constructorInjector; 296 if (localInjector == null) { 297 throw new IllegalStateException("Constructor not ready"); 298 } 299 300 if (!linked && failIfNotLinked) { 301 throw InternalProvisionException.jitDisabled(key); 302 } 303 304 // This may not actually be safe because it could return a super type of T (if that's all the 305 // client needs), but it should be OK in practice thanks to the wonders of erasure. 306 return (T) localInjector.construct(context, dependency, provisionCallback); 307 } 308 } 309 } 310