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