Revision: c75677752630
Author: Sam Berlin <[email protected]>
Date: Thu May 31 16:54:04 2012
Log: Add Binder.requireAtInjectOnConstructors, to force Guice to
require @Inject annotations on constructors.
Revision created by MOE tool push_codebase.
MOE_MIGRATION=4906
http://code.google.com/p/google-guice/source/detail?r=c75677752630
Added:
/core/src/com/google/inject/spi/RequireAtInjectOnConstructorsOption.java
/core/test/com/google/inject/RequireAtInjectOnConstructorsTest.java
Modified:
/core/src/com/google/inject/Binder.java
/core/src/com/google/inject/internal/BindingProcessor.java
/core/src/com/google/inject/internal/ConstructorBindingImpl.java
/core/src/com/google/inject/internal/Errors.java
/core/src/com/google/inject/internal/InjectorImpl.java
/core/src/com/google/inject/internal/InjectorOptionsProcessor.java
/core/src/com/google/inject/spi/DefaultElementVisitor.java
/core/src/com/google/inject/spi/ElementVisitor.java
/core/src/com/google/inject/spi/Elements.java
/core/src/com/google/inject/spi/InjectionPoint.java
/core/test/com/google/inject/AllTests.java
=======================================
--- /dev/null
+++
/core/src/com/google/inject/spi/RequireAtInjectOnConstructorsOption.java
Thu May 31 16:54:04 2012
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2012 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.spi;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.inject.Binder;
+import com.google.inject.Inject;
+
+/**
+ * A request to require explicit {@literal @}{@link Inject} annotations on
constructors.
+ *
+ * @author [email protected] (Sam Berlin)
+ * @since 4.0
+ */
+public final class RequireAtInjectOnConstructorsOption implements Element {
+ private final Object source;
+
+ RequireAtInjectOnConstructorsOption(Object source) {
+ this.source = checkNotNull(source, "source");
+ }
+
+ public Object getSource() {
+ return source;
+ }
+
+ public void applyTo(Binder binder) {
+ binder.withSource(getSource()).requireAtInjectOnConstructors();
+ }
+
+ public <T> T acceptVisitor(ElementVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+}
=======================================
--- /dev/null
+++ /core/test/com/google/inject/RequireAtInjectOnConstructorsTest.java Thu
May 31 16:54:04 2012
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 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;
+
+/**
+ * Tests for {@link Binder#requireAtInjectOnConstructors()}
+ *
+ * @author [email protected] (Sam Berlin)
+ */
+public class RequireAtInjectOnConstructorsTest extends TestCase {
+
+ public void testNoCxtors_explicitBinding() {
+ try {
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(NoCxtors.class);
+ binder().requireAtInjectOnConstructors();
+ }
+ });
+ fail();
+ } catch (CreationException ce) {
+ assertEquals(1, ce.getErrorMessages().size());
+ Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors,
but "
+ + NoCxtors.class.getName() + " has no constructors annotated
with @Inject",
+ "at " + RequireAtInjectOnConstructorsTest.class.getName()
+ "$", "configure");
+ }
+ }
+
+ public void testNoCxtors_jitBinding() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ binder().requireAtInjectOnConstructors();
+ }
+ });
+ try {
+ injector.getInstance(NoCxtors.class);
+ fail();
+ } catch (ConfigurationException ce) {
+ Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors,
but "
+ + NoCxtors.class.getName() + " has no constructors annotated
with @Inject",
+ "while locating " + NoCxtors.class.getName());
+ }
+ }
+
+ public void testNoCxtors_implicitBinding() {
+ try {
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Interface.class).to(NoCxtors.class);
+ binder().requireAtInjectOnConstructors();
+ }
+ });
+ fail();
+ } catch (CreationException ce) {
+ assertEquals(1, ce.getErrorMessages().size());
+ Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors,
but "
+ + NoCxtors.class.getName() + " has no constructors annotated
with @Inject",
+ "at " + RequireAtInjectOnConstructorsTest.class.getName()
+ "$", "configure");
+ }
+ }
+
+ public void testNoCxtors_inheritedByPrivateModules() {
+ try {
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ binder().requireAtInjectOnConstructors();
+ install(new PrivateModule() {
+ @Override
+ protected void configure() {
+ bind(NoCxtors.class);
+ }
+ });
+ }
+ });
+ fail();
+ } catch (CreationException ce) {
+ assertEquals(1, ce.getErrorMessages().size());
+ Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors,
but "
+ + NoCxtors.class.getName() + " has no constructors annotated
with @Inject",
+ "at " + RequireAtInjectOnConstructorsTest.class.getName()
+ "$", "configure");
+ }
+ }
+
+ public void testNoCxtors_accumulatesAllErrors() {
+ try {
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(NoCxtors.class);
+ bind(AnotherNoCxtors.class);
+ binder().requireAtInjectOnConstructors();
+ }
+ });
+ fail();
+ } catch (CreationException ce) {
+ assertEquals(2, ce.getErrorMessages().size());
+ Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors,
but "
+ + NoCxtors.class.getName() + " has no constructors annotated
with @Inject",
+ "at " + RequireAtInjectOnConstructorsTest.class.getName()
+ "$", "configure",
+ "2) Explicit @Inject annotations are required on constructors,
but "
+ + AnotherNoCxtors.class.getName() + " has no constructors
annotated with @Inject",
+ "at " + RequireAtInjectOnConstructorsTest.class.getName()
+ "$", "configure");
+ }
+ }
+
+ public void testNoCxtors_separateOptionsForPrivateModules() {
+ try {
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(AnotherNoCxtors.class);
+ install(new PrivateModule() {
+ @Override
+ protected void configure() {
+ binder().requireAtInjectOnConstructors();
+ bind(NoCxtors.class);
+ }
+ });
+ }
+ });
+ fail();
+ } catch (CreationException ce) {
+ // This is testing that the parent module doesn't fail because it
isn't included
+ // in the error message.
+ assertEquals(1, ce.getErrorMessages().size());
+ Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors,
but "
+ + NoCxtors.class.getName() + " has no constructors annotated
with @Inject",
+ "at " + RequireAtInjectOnConstructorsTest.class.getName()
+ "$", "configure");
+ }
+ }
+
+ public void testManyConstructorsButNoneWithAtInject() {
+ try {
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(ManyConstructors.class);
+ binder().requireAtInjectOnConstructors();
+ }
+ });
+ fail();
+ } catch (CreationException ce) {
+ assertEquals(1, ce.getErrorMessages().size());
+ Asserts.assertContains(ce.getMessage(),
+ "1) Explicit @Inject annotations are required on constructors,
but "
+ + ManyConstructors.class.getName() + " has no constructors
annotated with @Inject",
+ "at " + RequireAtInjectOnConstructorsTest.class.getName()
+ "$", "configure");
+ }
+ }
+
+ public void testRequireAtInjectStillAllowsToConstructorBindings() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ try {
+ bind(ManyConstructors.class)
+
.toConstructor(ManyConstructors.class.getDeclaredConstructor());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ binder().requireAtInjectOnConstructors();
+ }
+ });
+ injector.getInstance(ManyConstructors.class);
+ }
+
+ private static interface Interface {}
+ private static class NoCxtors implements Interface {}
+ private static class AnotherNoCxtors {}
+ private static class ManyConstructors {
+ @SuppressWarnings("unused") ManyConstructors() {}
+ @SuppressWarnings("unused") ManyConstructors(String a) {}
+ @SuppressWarnings("unused") ManyConstructors(int a) {}
+ }
+}
=======================================
--- /core/src/com/google/inject/Binder.java Sun Feb 26 18:23:19 2012
+++ /core/src/com/google/inject/Binder.java Thu May 31 16:54:04 2012
@@ -470,4 +470,17 @@
* @since 3.0
*/
void disableCircularProxies();
-}
+
+ /**
+ * Requires that a {@literal @}{@link Inject} annotation exists on a
constructor in order for
+ * Guice to consider it an eligible injectable class. By default, Guice
will inject classes that
+ * have a no-args constructor if no {@literal @}{@link Inject}
annotation exists on any
+ * constructor.
+ * <p>
+ * If the class is bound using {@link
LinkedBindingBuilder#toConstructor}, Guice will still inject
+ * that constructor regardless of annotations.
+ *
+ * @since 4.0
+ */
+ void requireAtInjectOnConstructors();
+}
=======================================
--- /core/src/com/google/inject/internal/BindingProcessor.java Sun Feb 26
18:23:19 2012
+++ /core/src/com/google/inject/internal/BindingProcessor.java Thu May 31
16:54:04 2012
@@ -72,7 +72,7 @@
prepareBinding();
try {
ConstructorBindingImpl<T> onInjector =
ConstructorBindingImpl.create(injector, key,
- binding.getConstructor(), source, scoping, errors, false);
+ binding.getConstructor(), source, scoping, errors, false,
false);
scheduleInitialization(onInjector);
putBinding(onInjector);
} catch (ErrorsException e) {
=======================================
--- /core/src/com/google/inject/internal/ConstructorBindingImpl.java Sun
Feb 26 18:23:19 2012
+++ /core/src/com/google/inject/internal/ConstructorBindingImpl.java Thu
May 31 16:54:04 2012
@@ -23,6 +23,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.ConfigurationException;
+import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes;
@@ -71,7 +72,7 @@
*/
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector,
Key<T> key,
InjectionPoint constructorInjector, Object source, Scoping scoping,
Errors errors,
- boolean failIfNotLinked)
+ boolean failIfNotLinked, boolean failIfNotExplicit)
throws ErrorsException {
int numErrors = errors.size();
@@ -96,6 +97,9 @@
if (constructorInjector == null) {
try {
constructorInjector =
InjectionPoint.forConstructorOf(key.getTypeLiteral());
+ if (failIfNotExplicit && !hasAtInject((Constructor)
constructorInjector.getMember())) {
+ errors.atInjectRequired(rawType);
+ }
} catch (ConfigurationException e) {
throw errors.merge(e.getErrorMessages()).toException();
}
@@ -120,6 +124,12 @@
return new ConstructorBindingImpl<T>(
injector, key, source, scopedFactory, scoping, factoryFactory,
constructorInjector);
}
+
+ /** Returns true if the inject annotation is on the constructor. */
+ private static boolean hasAtInject(Constructor cxtor) {
+ return cxtor.isAnnotationPresent(Inject.class)
+ || cxtor.isAnnotationPresent(javax.inject.Inject.class);
+ }
@SuppressWarnings("unchecked") // the result type always agrees with the
ConstructorInjector type
public void initialize(InjectorImpl injector, Errors errors) throws
ErrorsException {
=======================================
--- /core/src/com/google/inject/internal/Errors.java Fri Sep 9 14:19:11
2011
+++ /core/src/com/google/inject/internal/Errors.java Thu May 31 16:54:04
2012
@@ -138,6 +138,13 @@
public Errors jitDisabled(Key key) {
return addMessage("Explicit bindings are required and %s is not
explicitly bound.", key);
}
+
+ public Errors atInjectRequired(Class clazz) {
+ return addMessage(
+ "Explicit @Inject annotations are required on constructors,"
+ + " but %s has no constructors annotated with @Inject.",
+ clazz);
+ }
public Errors converterReturnedNull(String stringValue, Object source,
TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) {
@@ -647,17 +654,17 @@
private static final Collection<Converter<?>> converters =
ImmutableList.of(
new Converter<Class>(Class.class) {
- public String toString(Class c) {
+ @Override public String toString(Class c) {
return c.getName();
}
},
new Converter<Member>(Member.class) {
- public String toString(Member member) {
+ @Override public String toString(Member member) {
return Classes.toString(member);
}
},
new Converter<Key>(Key.class) {
- public String toString(Key key) {
+ @Override public String toString(Key key) {
if (key.getAnnotationType() != null) {
return key.getTypeLiteral() + " annotated with "
+ (key.getAnnotation() != null ? key.getAnnotation() :
key.getAnnotationType());
=======================================
--- /core/src/com/google/inject/internal/InjectorImpl.java Sun Feb 26
18:23:19 2012
+++ /core/src/com/google/inject/internal/InjectorImpl.java Thu May 31
16:54:04 2012
@@ -71,11 +71,14 @@
final Stage stage;
final boolean jitDisabled;
final boolean disableCircularProxies;
-
- InjectorOptions(Stage stage, boolean jitDisabled, boolean
disableCircularProxies) {
+ final boolean atInjectRequired;
+
+ InjectorOptions(Stage stage, boolean jitDisabled, boolean
disableCircularProxies,
+ boolean atInjectRequired) {
this.stage = stage;
this.jitDisabled = jitDisabled;
this.disableCircularProxies = disableCircularProxies;
+ this.atInjectRequired = atInjectRequired;
}
@Override
@@ -84,6 +87,7 @@
.add("stage", stage)
.add("jitDisabled", jitDisabled)
.add("disableCircularProxies", disableCircularProxies)
+ .add("atInjectRequired", atInjectRequired)
.toString();
}
}
@@ -179,7 +183,7 @@
if(isProvider(key)) {
try {
// This is safe because isProvider above ensures that T is a
Provider<?>
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "cast"})
Key<?> providedKey = (Key<?>)getProvidedKey((Key)key, new
Errors());
if(getExistingBinding(providedKey) != null) {
return getBinding(key);
@@ -651,7 +655,14 @@
}
- return ConstructorBindingImpl.create(this, key, null, source, scoping,
errors, jitBinding && options.jitDisabled);
+ return ConstructorBindingImpl.create(this,
+ key,
+ null, /* use default constructor */
+ source,
+ scoping,
+ errors,
+ jitBinding && options.jitDisabled,
+ options.atInjectRequired);
}
/**
@@ -813,7 +824,7 @@
if (isProvider(key)) {
// These casts are safe. We know T extends Provider<X> and that
given Key<Provider<X>>,
// createProviderBinding() will return BindingImpl<Provider<X>>.
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "cast"})
BindingImpl<T> binding = (BindingImpl<T>)
createProviderBinding((Key) key, errors);
return binding;
}
@@ -822,7 +833,7 @@
if (isMembersInjector(key)) {
// These casts are safe. T extends MembersInjector<X> and that given
Key<MembersInjector<X>>,
// createMembersInjectorBinding() will return
BindingImpl<MembersInjector<X>>.
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "cast"})
BindingImpl<T> binding = (BindingImpl<T>)
createMembersInjectorBinding((Key) key, errors);
return binding;
}
=======================================
--- /core/src/com/google/inject/internal/InjectorOptionsProcessor.java Tue
Jun 28 16:50:03 2011
+++ /core/src/com/google/inject/internal/InjectorOptionsProcessor.java Thu
May 31 16:54:04 2012
@@ -22,6 +22,7 @@
import com.google.inject.Stage;
import com.google.inject.internal.InjectorImpl.InjectorOptions;
import com.google.inject.spi.DisableCircularProxiesOption;
+import com.google.inject.spi.RequireAtInjectOnConstructorsOption;
import com.google.inject.spi.RequireExplicitBindingsOption;
/**
@@ -33,6 +34,7 @@
private boolean disableCircularProxies = false;
private boolean jitDisabled = false;
+ private boolean atInjectRequired = false;
InjectorOptionsProcessor(Errors errors) {
super(errors);
@@ -49,6 +51,12 @@
jitDisabled = true;
return true;
}
+
+ @Override
+ public Boolean visit(RequireAtInjectOnConstructorsOption option) {
+ atInjectRequired = true;
+ return true;
+ }
InjectorOptions getOptions(Stage stage, InjectorOptions parentOptions) {
checkNotNull(stage, "stage must be set");
@@ -56,13 +64,15 @@
return new InjectorOptions(
stage,
jitDisabled,
- disableCircularProxies);
+ disableCircularProxies,
+ atInjectRequired);
} else {
checkState(stage == parentOptions.stage, "child & parent stage don't
match");
return new InjectorOptions(
stage,
jitDisabled || parentOptions.jitDisabled,
- disableCircularProxies || parentOptions.disableCircularProxies);
+ disableCircularProxies || parentOptions.disableCircularProxies,
+ atInjectRequired || parentOptions.atInjectRequired);
}
}
=======================================
--- /core/src/com/google/inject/spi/DefaultElementVisitor.java Sun Jun 5
11:32:05 2011
+++ /core/src/com/google/inject/spi/DefaultElementVisitor.java Thu May 31
16:54:04 2012
@@ -94,4 +94,8 @@
public V visit(RequireExplicitBindingsOption option) {
return visitOther(option);
}
-}
+
+ public V visit(RequireAtInjectOnConstructorsOption option) {
+ return visitOther(option);
+ }
+}
=======================================
--- /core/src/com/google/inject/spi/ElementVisitor.java Sun Jun 5 11:32:05
2011
+++ /core/src/com/google/inject/spi/ElementVisitor.java Thu May 31 16:54:04
2012
@@ -17,6 +17,7 @@
package com.google.inject.spi;
import com.google.inject.Binding;
+import com.google.inject.Inject;
/**
* Visit elements.
@@ -105,4 +106,11 @@
* @since 3.0
*/
V visit(DisableCircularProxiesOption option);
-}
+
+ /**
+ * Visit a require explicit {@literal @}{@link Inject} command.
+ *
+ * @since 4.0
+ */
+ V visit(RequireAtInjectOnConstructorsOption option);
+}
=======================================
--- /core/src/com/google/inject/spi/Elements.java Sun Feb 26 18:23:19 2012
+++ /core/src/com/google/inject/spi/Elements.java Thu May 31 16:54:04 2012
@@ -315,6 +315,10 @@
public void requireExplicitBindings() {
elements.add(new RequireExplicitBindingsOption(getSource()));
}
+
+ public void requireAtInjectOnConstructors() {
+ elements.add(new RequireAtInjectOnConstructorsOption(getSource()));
+ }
public void expose(Key<?> key) {
exposeInternal(key);
=======================================
--- /core/src/com/google/inject/spi/InjectionPoint.java Fri Sep 9 14:19:11
2011
+++ /core/src/com/google/inject/spi/InjectionPoint.java Thu May 31 16:54:04
2012
@@ -444,6 +444,7 @@
this.field = field;
}
+ @Override
InjectionPoint toInjectionPoint() {
return new InjectionPoint(declaringType, field, optional);
}
@@ -463,6 +464,7 @@
this.method = method;
}
+ @Override
InjectionPoint toInjectionPoint() {
return new InjectionPoint(declaringType, method, optional);
}
=======================================
--- /core/test/com/google/inject/AllTests.java Thu Jul 7 17:34:16 2011
+++ /core/test/com/google/inject/AllTests.java Thu May 31 16:54:04 2012
@@ -36,6 +36,7 @@
import com.google.inject.util.NoopOverrideTest;
import com.google.inject.util.ProvidersTest;
import com.google.inject.util.TypesTest;
+
import com.googlecode.guice.GuiceTck;
import com.googlecode.guice.Jsr330Test;
@@ -92,6 +93,7 @@
// ProxyFactoryTest is AOP-only
suite.addTestSuite(ReflectionTest.class);
suite.addTestSuite(RequestInjectionTest.class);
+ suite.addTestSuite(RequireAtInjectOnConstructorsTest.class);
suite.addTestSuite(ScopesTest.class);
suite.addTestSuite(SerializationTest.class);
suite.addTestSuite(SuperclassTest.class);
--
You received this message because you are subscribed to the Google Groups
"google-guice-dev" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/google-guice-dev?hl=en.