blob: 1ff4be1f3e9b50fe15fdc0c50314bfad3547b8b2 [file] [log] [blame]
crazybobleeb8cf1e52007-02-02 21:48:16 +00001/**
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 */
crazybobleee3adfd62007-02-02 21:30:08 +000016
limpbizkit5ae41eb2009-06-06 17:51:27 +000017package com.google.inject.internal;
crazybobleee3adfd62007-02-02 21:30:08 +000018
sberlind9c913a2011-06-26 21:02:54 +000019import com.google.common.collect.ImmutableSet;
sberlinb7a02b02011-07-08 00:34:16 +000020import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
limpbizkita843a952009-04-08 22:24:55 +000021import com.google.inject.spi.InjectionPoint;
sberlinb7a02b02011-07-08 00:34:16 +000022
crazybobleee3adfd62007-02-02 21:30:08 +000023import java.lang.reflect.InvocationTargetException;
limpbizkit9b8bdc72009-06-07 19:37:28 +000024import java.util.Set;
crazybobleee3adfd62007-02-02 21:30:08 +000025
26/**
limpbizkit06898062008-11-02 05:14:55 +000027 * Creates instances using an injectable constructor. After construction, all injectable fields and
28 * methods are injected.
crazybobleee3adfd62007-02-02 21:30:08 +000029 *
30 * @author crazybob@google.com (Bob Lee)
kevinb9na99dca72007-02-11 04:48:57 +000031 */
limpbizkit5ae41eb2009-06-06 17:51:27 +000032final class ConstructorInjector<T> {
crazybobleee3adfd62007-02-02 21:30:08 +000033
limpbizkita843a952009-04-08 22:24:55 +000034 private final ImmutableSet<InjectionPoint> injectableMembers;
limpbizkite89c49e2009-05-06 01:02:14 +000035 private final SingleParameterInjector<?>[] parameterInjectors;
limpbizkit03b81a62009-03-18 05:34:39 +000036 private final ConstructionProxy<T> constructionProxy;
limpbizkit7cef5b02009-03-29 21:16:51 +000037 private final MembersInjectorImpl<T> membersInjector;
crazybobleee3adfd62007-02-02 21:30:08 +000038
limpbizkit9b8bdc72009-06-07 19:37:28 +000039 ConstructorInjector(Set<InjectionPoint> injectableMembers,
limpbizkita843a952009-04-08 22:24:55 +000040 ConstructionProxy<T> constructionProxy,
limpbizkite89c49e2009-05-06 01:02:14 +000041 SingleParameterInjector<?>[] parameterInjectors,
limpbizkit9b8bdc72009-06-07 19:37:28 +000042 MembersInjectorImpl<T> membersInjector) {
43 this.injectableMembers = ImmutableSet.copyOf(injectableMembers);
limpbizkit03b81a62009-03-18 05:34:39 +000044 this.constructionProxy = constructionProxy;
45 this.parameterInjectors = parameterInjectors;
limpbizkit7cef5b02009-03-29 21:16:51 +000046 this.membersInjector = membersInjector;
limpbizkita843a952009-04-08 22:24:55 +000047 }
48
49 public ImmutableSet<InjectionPoint> getInjectableMembers() {
50 return injectableMembers;
51 }
52
limpbizkita843a952009-04-08 22:24:55 +000053 ConstructionProxy<T> getConstructionProxy() {
54 return constructionProxy;
crazybobleee3adfd62007-02-02 21:30:08 +000055 }
56
crazybobleee3adfd62007-02-02 21:30:08 +000057 /**
kevinb9na99dca72007-02-11 04:48:57 +000058 * Construct an instance. Returns {@code Object} instead of {@code T} because
59 * it may return a proxy.
crazybobleee3adfd62007-02-02 21:30:08 +000060 */
sberlin132a5db2011-06-05 18:32:05 +000061 Object construct(final Errors errors, final InternalContext context,
timofeyb5e6c9332015-04-20 12:45:07 -070062 Class<?> expectedType,
sberlin132a5db2011-06-05 18:32:05 +000063 ProvisionListenerStackCallback<T> provisionCallback)
limpbizkit163c48a2008-06-16 02:58:08 +000064 throws ErrorsException {
sberlin132a5db2011-06-05 18:32:05 +000065 final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
crazybobleee3adfd62007-02-02 21:30:08 +000066
67 // We have a circular reference between constructors. Return a proxy.
68 if (constructionContext.isConstructing()) {
timofeyb5e6c9332015-04-20 12:45:07 -070069 // TODO (crazybob): if we can't proxy this object, can we proxy the other object?
70 return constructionContext.createProxy(
71 errors, context.getInjectorOptions(), expectedType);
crazybobleee3adfd62007-02-02 21:30:08 +000072 }
73
74 // If we're re-entering this factory while injecting fields or methods,
75 // return the same instance. This prevents infinite loops.
76 T t = constructionContext.getCurrentReference();
77 if (t != null) {
78 return t;
79 }
80
Sam Berlind51292d2012-02-26 21:23:19 -050081 constructionContext.startConstruction();
crazybobleee3adfd62007-02-02 21:30:08 +000082 try {
sberlin132a5db2011-06-05 18:32:05 +000083 // Optimization: Don't go through the callback stack if we have no listeners.
84 if (!provisionCallback.hasListeners()) {
timofeyb5e6c9332015-04-20 12:45:07 -070085 return provision(errors, context, constructionContext);
sberlin132a5db2011-06-05 18:32:05 +000086 } else {
sberlinba75f352011-06-12 21:54:43 +000087 return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
sberlin132a5db2011-06-05 18:32:05 +000088 public T call() throws ErrorsException {
89 return provision(errors, context, constructionContext);
90 }
91 });
92 }
93 } finally {
Sam Berlind51292d2012-02-26 21:23:19 -050094 constructionContext.finishConstruction();
sberlin132a5db2011-06-05 18:32:05 +000095 }
96 }
97
98 /** Provisions a new T. */
99 private T provision(Errors errors, InternalContext context,
100 ConstructionContext<T> constructionContext) throws ErrorsException {
101 try {
sberlin132a5db2011-06-05 18:32:05 +0000102 T t;
crazybobleee3adfd62007-02-02 21:30:08 +0000103 try {
limpbizkit06898062008-11-02 05:14:55 +0000104 Object[] parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
kevinb9n48d13072007-02-12 18:21:26 +0000105 t = constructionProxy.newInstance(parameters);
crazybobleee3adfd62007-02-02 21:30:08 +0000106 constructionContext.setProxyDelegates(t);
limpbizkit06898062008-11-02 05:14:55 +0000107 } finally {
crazybobleee3adfd62007-02-02 21:30:08 +0000108 constructionContext.finishConstruction();
109 }
Christian Edward Gruber9e2d95b2013-06-26 17:43:11 -0700110
limpbizkita843a952009-04-08 22:24:55 +0000111 // Store reference. If an injector re-enters this factory, they'll get the same reference.
crazybobleee3adfd62007-02-02 21:30:08 +0000112 constructionContext.setCurrentReference(t);
Christian Edward Gruber9e2d95b2013-06-26 17:43:11 -0700113
sberlin97c22712010-02-05 21:12:05 +0000114 membersInjector.injectMembers(t, errors, context, false);
limpbizkita843a952009-04-08 22:24:55 +0000115 membersInjector.notifyListeners(t, errors);
Christian Edward Gruber9e2d95b2013-06-26 17:43:11 -0700116
crazybobleee3adfd62007-02-02 21:30:08 +0000117 return t;
limpbizkita6e0e782008-09-03 06:19:56 +0000118 } catch (InvocationTargetException userException) {
limpbizkit9dc32d42008-06-15 11:29:10 +0000119 Throwable cause = userException.getCause() != null
120 ? userException.getCause()
121 : userException;
limpbizkita6e0e782008-09-03 06:19:56 +0000122 throw errors.withSource(constructionProxy.getInjectionPoint())
123 .errorInjectingConstructor(cause).toException();
Christian Edward Gruber2cc8ce92013-05-16 10:53:11 -0700124 } finally {
125 constructionContext.removeCurrentReference();
crazybobleee3adfd62007-02-02 21:30:08 +0000126 }
127 }
crazybobleee3adfd62007-02-02 21:30:08 +0000128}