Added support for surrogate injection annotations.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@132 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/ConstructorInjector.java b/src/com/google/inject/ConstructorInjector.java
index 24f56ed..d36ea31 100644
--- a/src/com/google/inject/ConstructorInjector.java
+++ b/src/com/google/inject/ConstructorInjector.java
@@ -16,6 +16,9 @@
package com.google.inject;
+import com.google.inject.util.SurrogateAnnotations;
+import com.google.inject.util.DuplicateAnnotationException;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -31,6 +34,9 @@
final ContainerImpl.ParameterInjector<?>[] parameterInjectors;
final ConstructionProxy<T> constructionProxy;
+ /** Annotation on the constructor. */
+ Inject inject;
+
ConstructorInjector(ContainerImpl container, Class<T> implementation) {
this.implementation = implementation;
Constructor<T> constructor = findConstructorIn(container, implementation);
@@ -53,7 +59,6 @@
ContainerImpl.ParameterInjector<?>[] createParameterInjector(
ContainerImpl container, Constructor<T> constructor) {
try {
- Inject inject = constructor.getAnnotation(Inject.class);
return inject == null
? null // default constructor.
: container.getParametersInjectors(
@@ -76,13 +81,23 @@
Constructor<T>[] constructors
= (Constructor<T>[]) implementation.getDeclaredConstructors();
for (Constructor<T> constructor : constructors) {
- if (constructor.getAnnotation(Inject.class) != null) {
+ Inject inject = null;
+ try {
+ inject = SurrogateAnnotations.findAnnotation(Inject.class, constructor);
+ } catch (DuplicateAnnotationException e) {
+ container.errorHandler.handle(ErrorMessages.DUPLICATE_ANNOTATIONS,
+ Inject.class.getSimpleName(), constructor, e.getFirst(),
+ e.getSecond());
+ }
+
+ if (inject != null) {
if (found != null) {
container.errorHandler.handle(
ErrorMessages.TOO_MANY_CONSTRUCTORS, implementation);
return ContainerImpl.invalidConstructor();
}
found = constructor;
+ this.inject = inject;
}
}
if (found != null) {
diff --git a/src/com/google/inject/ContainerImpl.java b/src/com/google/inject/ContainerImpl.java
index 582684e..7f49d68 100644
--- a/src/com/google/inject/ContainerImpl.java
+++ b/src/com/google/inject/ContainerImpl.java
@@ -21,6 +21,8 @@
import com.google.inject.util.ReferenceCache;
import com.google.inject.util.Strings;
import com.google.inject.util.ToStringBuilder;
+import com.google.inject.util.SurrogateAnnotations;
+import com.google.inject.util.DuplicateAnnotationException;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
@@ -334,7 +336,15 @@
InjectorFactory<M> injectorFactory) {
for (M member : members) {
if (isStatic(member) == statics) {
- Inject inject = member.getAnnotation(Inject.class);
+ Inject inject = null;
+ try {
+ inject = SurrogateAnnotations.findAnnotation(Inject.class, member);
+ } catch (DuplicateAnnotationException e) {
+ errorHandler.handle(ErrorMessages.DUPLICATE_ANNOTATIONS,
+ Inject.class.getSimpleName(), member, e.getFirst(),
+ e.getSecond());
+ }
+
if (inject != null) {
try {
injectors.add(injectorFactory.create(this, member, inject.value()));
@@ -463,18 +473,27 @@
= Arrays.asList(annotations).iterator();
int index = 0;
for (Type parameterType : parameterTypes) {
- Inject annotation = findInject(annotationsIterator.next());
+ Annotation[] parameterAnnotations = annotationsIterator.next();
+ Inject inject = null;
+ try {
+ inject = SurrogateAnnotations.findAnnotation(Inject.class,
+ parameterAnnotations);
+ } catch (DuplicateAnnotationException e) {
+ errorHandler.handle(ErrorMessages.DUPLICATE_ANNOTATIONS,
+ Inject.class.getSimpleName(), member, e.getFirst(),
+ e.getSecond());
+ }
String name;
if (defaultNameOverridden) {
name = defaultName;
- if (annotation != null) {
+ if (inject != null) {
errorHandler.handle(
ErrorMessages.NAME_ON_MEMBER_AND_PARAMETER, member);
}
}
else {
- name = annotation == null ? defaultName : annotation.value();
+ name = inject == null ? defaultName : inject.value();
}
Key<?> key = Key.get(parameterType, name);
@@ -497,18 +516,6 @@
return new ParameterInjector<T>(externalContext, factory);
}
- /**
- * Finds the {@link Inject} annotation in an array of annotations.
- */
- Inject findInject(Annotation[] annotations) {
- for (Annotation annotation : annotations) {
- if (annotation.annotationType() == Inject.class) {
- return Inject.class.cast(annotation);
- }
- }
- return null;
- }
-
static class MethodInjector implements Injector {
final FastMethod fastMethod;
diff --git a/src/com/google/inject/Inject.java b/src/com/google/inject/Inject.java
index 087d6ca..10e4f7c 100644
--- a/src/com/google/inject/Inject.java
+++ b/src/com/google/inject/Inject.java
@@ -16,6 +16,7 @@
package com.google.inject;
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
@@ -26,11 +27,13 @@
/**
* <p>Annotates members and parameters which should have their value[s]
- * injected.
+ * injected. If applied to another annotation type, that annotation type
+ * can act as a surrogate and save you from having to repeat the dependency
+ * name over and over.
*
* @author crazybob@google.com (Bob Lee)
*/
-@Target({ METHOD, CONSTRUCTOR, FIELD, PARAMETER })
+@Target({ METHOD, CONSTRUCTOR, FIELD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface Inject {
diff --git a/src/com/google/inject/util/SurrogateAnnotations.java b/src/com/google/inject/util/SurrogateAnnotations.java
index 3530f07..d461081 100644
--- a/src/com/google/inject/util/SurrogateAnnotations.java
+++ b/src/com/google/inject/util/SurrogateAnnotations.java
@@ -33,9 +33,9 @@
public class SurrogateAnnotations {
/**
- * Finds an annotation of the given type on the given element. Looks for
- * the annotation directly on the element as well as on annotations on the
- * element non-recursively.
+ * Finds an annotation of the given type on the given element. Looks for the
+ * annotation directly on the element as well as on other annotations on the
+ * element.
*
* @returns an instance of the annotation or {@code null} if none is found
* @throws DuplicateAnnotationException if more than one annotation is found
@@ -43,10 +43,24 @@
public static <A extends Annotation> A findAnnotation(
Class<A> annotationType, AnnotatedElement element)
throws DuplicateAnnotationException {
+ return findAnnotation(annotationType, element.getAnnotations());
+ }
+
+ /**
+ * Finds an annotation of the given type in the given array of annotations.
+ * Looks for the annotation in the array as well as on other annotations in
+ * the array.
+ *
+ * @returns an instance of the annotation or {@code null} if none is found
+ * @throws DuplicateAnnotationException if more than one annotation is found
+ */
+ public static <A extends Annotation> A findAnnotation(
+ Class<A> annotationType, Annotation[] annotations)
+ throws DuplicateAnnotationException {
Annotation firstAnnotation = null;
A firstFound = null;
- for (Annotation annotation : element.getAnnotations()) {
+ for (Annotation annotation : annotations) {
A found = findAnnotation(annotationType, annotation);
if (found != null) {
if (firstFound == null) {
diff --git a/test/com/google/inject/AllTests.java b/test/com/google/inject/AllTests.java
index f761411..2e51369 100644
--- a/test/com/google/inject/AllTests.java
+++ b/test/com/google/inject/AllTests.java
@@ -49,6 +49,7 @@
suite.addTestSuite(ReflectionTest.class);
suite.addTestSuite(ScopesTest.class);
suite.addTestSuite(ImplicitBindingTest.class);
+ suite.addTestSuite(InjectTest.class);
suite.addTestSuite(QueryTest.class);
suite.addTestSuite(ProxyFactoryTest.class);
diff --git a/test/com/google/inject/InjectTest.java b/test/com/google/inject/InjectTest.java
new file mode 100644
index 0000000..7b50c9c
--- /dev/null
+++ b/test/com/google/inject/InjectTest.java
@@ -0,0 +1,71 @@
+/**
+ * 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;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class InjectTest extends TestCase {
+
+ public void testSurrogateInjection() throws ContainerCreationException {
+ ContainerBuilder builder = new ContainerBuilder();
+ Bar bar = new Bar(null);
+ builder.bind(Bar.class).named("bar").to(bar);
+ Container container = builder.create(false);
+ Foo foo = container.getInstance(Foo.class);
+ assertSame(bar, foo.field);
+ assertSame(bar, foo.fromConstructor);
+ assertSame(bar, foo.fromMethod);
+ assertSame(bar, foo.fromParameter);
+ }
+
+ @Retention(RUNTIME)
+ @Inject("bar")
+ @interface InjectBar {}
+
+ static class Foo {
+ @InjectBar Bar field;
+
+ Bar fromConstructor;
+ @InjectBar
+ public Foo(Bar fromConstructor) {
+ this.fromConstructor = fromConstructor;
+ }
+
+ Bar fromMethod;
+ @InjectBar
+ void setBar(Bar bar) {
+ fromMethod = bar;
+ }
+
+ Bar fromParameter;
+ @Inject
+ void setBar2(@InjectBar Bar bar) {
+ fromParameter= bar;
+ }
+ }
+
+ static class Bar {
+ // Not injectable.
+ Bar(String s) {}
+ }
+}