Created
July 7, 2014 19:13
-
-
Save gissuebot/29297c50e876ec28504f to your computer and use it in GitHub Desktop.
Migrated attachment for Guice issue 770, comment 0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 6c1c5f6d33e06481863a1a721fd2cf4087674797 Mon Sep 17 00:00:00 2001 | |
From: Jakub Narloch <jmnarloch@gmail.com> | |
Date: Mon, 9 Sep 2013 00:03:21 +0200 | |
Subject: [PATCH] Added functionality for runtime buinding override, by | |
creating new child injector that can override/or hide the | |
bindings of it's parent injector. | |
--- | |
core/src/com/google/inject/Binder.java | 2 + | |
.../inject/internal/AbstractBindingProcessor.java | 7 +- | |
.../google/inject/internal/BindingProcessor.java | 4 +- | |
.../inject/internal/InjectorOptionsProcessor.java | 1 + | |
.../com/google/inject/internal/InjectorShell.java | 9 +- | |
.../com/google/inject/internal/ModuleOptions.java | 48 ++++++++ | |
.../inject/internal/ModuleOptionsProcessor.java | 44 ++++++++ | |
.../internal/UntargettedBindingProcessor.java | 4 +- | |
.../google/inject/spi/DefaultElementVisitor.java | 4 + | |
core/src/com/google/inject/spi/ElementVisitor.java | 7 ++ | |
core/src/com/google/inject/spi/Elements.java | 6 +- | |
.../google/inject/spi/OverrideBindingOption.java | 50 +++++++++ | |
.../com/google/inject/OverrideBindingTest.java | 123 +++++++++++++++++++++ | |
13 files changed, 300 insertions(+), 9 deletions(-) | |
create mode 100644 core/src/com/google/inject/internal/ModuleOptions.java | |
create mode 100644 core/src/com/google/inject/internal/ModuleOptionsProcessor.java | |
create mode 100644 core/src/com/google/inject/spi/OverrideBindingOption.java | |
create mode 100644 core/test/com/google/inject/OverrideBindingTest.java | |
diff --git a/core/src/com/google/inject/Binder.java b/core/src/com/google/inject/Binder.java | |
index dae3812..c185af5 100644 | |
--- a/core/src/com/google/inject/Binder.java | |
+++ b/core/src/com/google/inject/Binder.java | |
@@ -493,4 +493,6 @@ public interface Binder { | |
* @since 4.0 | |
*/ | |
void requireExactBindingAnnotations(); | |
+ | |
+ void allowBindingOverride(); | |
} | |
diff --git a/core/src/com/google/inject/internal/AbstractBindingProcessor.java b/core/src/com/google/inject/internal/AbstractBindingProcessor.java | |
index 72dae2b..ecfdb93 100644 | |
--- a/core/src/com/google/inject/internal/AbstractBindingProcessor.java | |
+++ b/core/src/com/google/inject/internal/AbstractBindingProcessor.java | |
@@ -56,10 +56,13 @@ abstract class AbstractBindingProcessor extends AbstractProcessor { | |
TypeLiteral.class); | |
protected final ProcessedBindingData bindingData; | |
+ | |
+ protected final ModuleOptions moduleOptions; | |
- AbstractBindingProcessor(Errors errors, ProcessedBindingData bindingData) { | |
+ AbstractBindingProcessor(Errors errors, ProcessedBindingData bindingData, ModuleOptions moduleOptions) { | |
super(errors); | |
this.bindingData = bindingData; | |
+ this.moduleOptions = moduleOptions; | |
} | |
protected <T> UntargettedBindingImpl<T> invalidBinding( | |
@@ -119,7 +122,7 @@ abstract class AbstractBindingProcessor extends AbstractProcessor { | |
// If no original at this level, the original was on a parent, and we don't | |
// allow deduplication between parents & children. | |
if(original == null) { | |
- return false; | |
+ return moduleOptions.isOverrideBindings(); | |
} else { | |
return original.equals(binding); | |
} | |
diff --git a/core/src/com/google/inject/internal/BindingProcessor.java b/core/src/com/google/inject/internal/BindingProcessor.java | |
index 7ce3ea0..067ab1e 100644 | |
--- a/core/src/com/google/inject/internal/BindingProcessor.java | |
+++ b/core/src/com/google/inject/internal/BindingProcessor.java | |
@@ -44,8 +44,8 @@ final class BindingProcessor extends AbstractBindingProcessor { | |
private final Initializer initializer; | |
- BindingProcessor(Errors errors, Initializer initializer, ProcessedBindingData bindingData) { | |
- super(errors, bindingData); | |
+ BindingProcessor(Errors errors, Initializer initializer, ProcessedBindingData bindingData, ModuleOptions moduleOptions) { | |
+ super(errors, bindingData, moduleOptions); | |
this.initializer = initializer; | |
} | |
diff --git a/core/src/com/google/inject/internal/InjectorOptionsProcessor.java b/core/src/com/google/inject/internal/InjectorOptionsProcessor.java | |
index 61cc6ee..8239940 100644 | |
--- a/core/src/com/google/inject/internal/InjectorOptionsProcessor.java | |
+++ b/core/src/com/google/inject/internal/InjectorOptionsProcessor.java | |
@@ -87,3 +87,4 @@ class InjectorOptionsProcessor extends AbstractProcessor { | |
} | |
} | |
+ | |
diff --git a/core/src/com/google/inject/internal/InjectorShell.java b/core/src/com/google/inject/internal/InjectorShell.java | |
index 4675b11..9e9ab18 100644 | |
--- a/core/src/com/google/inject/internal/InjectorShell.java | |
+++ b/core/src/com/google/inject/internal/InjectorShell.java | |
@@ -76,6 +76,7 @@ final class InjectorShell { | |
private InjectorImpl parent; | |
private InjectorOptions options; | |
+ private ModuleOptions moduleOptions; | |
private Stage stage; | |
/** null unless this exists in a {@link Binder#newPrivateBinder private environment} */ | |
@@ -139,6 +140,10 @@ final class InjectorShell { | |
InjectorOptionsProcessor optionsProcessor = new InjectorOptionsProcessor(errors); | |
optionsProcessor.process(null, elements); | |
options = optionsProcessor.getOptions(stage, options); | |
+ | |
+ ModuleOptionsProcessor moduleOptionsProcessor = new ModuleOptionsProcessor(errors); | |
+ moduleOptionsProcessor.process(null, elements); | |
+ moduleOptions = moduleOptionsProcessor.getOptions(); | |
InjectorImpl injector = new InjectorImpl(parent, state, options); | |
if (privateElements != null) { | |
@@ -181,8 +186,8 @@ final class InjectorShell { | |
// Process all normal bindings, then UntargettedBindings. | |
// This is necessary because UntargettedBindings can create JIT bindings | |
// and need all their other dependencies set up ahead of time. | |
- new BindingProcessor(errors, initializer, bindingData).process(injector, elements); | |
- new UntargettedBindingProcessor(errors, bindingData).process(injector, elements); | |
+ new BindingProcessor(errors, initializer, bindingData, moduleOptions).process(injector, elements); | |
+ new UntargettedBindingProcessor(errors, bindingData, moduleOptions).process(injector, elements); | |
stopwatch.resetAndLog("Binding creation"); | |
List<InjectorShell> injectorShells = Lists.newArrayList(); | |
diff --git a/core/src/com/google/inject/internal/ModuleOptions.java b/core/src/com/google/inject/internal/ModuleOptions.java | |
new file mode 100644 | |
index 0000000..a96836f | |
--- /dev/null | |
+++ b/core/src/com/google/inject/internal/ModuleOptions.java | |
@@ -0,0 +1,48 @@ | |
+/** | |
+ * Copyright (C) 2013 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.internal; | |
+ | |
+/** | |
+ * Gathers the modules options. | |
+ * | |
+ * @author jmnarloch@gmail.com (Jakub Narloch) | |
+ */ | |
+public class ModuleOptions { | |
+ | |
+ /** | |
+ * Flag indicating whether the binding override is enabled in the child module. | |
+ */ | |
+ private final boolean overrideBindings; | |
+ | |
+ /** | |
+ * Creates new instance of {@link ModuleOptions} class. | |
+ * | |
+ * @param overrideBindings whether to override the bindings | |
+ */ | |
+ public ModuleOptions(boolean overrideBindings) { | |
+ this.overrideBindings = overrideBindings; | |
+ } | |
+ | |
+ /** | |
+ * Returns whether the binding override has been enabled | |
+ * | |
+ * @return whether the binding override has been enabled | |
+ */ | |
+ public boolean isOverrideBindings() { | |
+ return overrideBindings; | |
+ } | |
+} | |
diff --git a/core/src/com/google/inject/internal/ModuleOptionsProcessor.java b/core/src/com/google/inject/internal/ModuleOptionsProcessor.java | |
new file mode 100644 | |
index 0000000..3ddf80e | |
--- /dev/null | |
+++ b/core/src/com/google/inject/internal/ModuleOptionsProcessor.java | |
@@ -0,0 +1,44 @@ | |
+/** | |
+ * Copyright (C) 2013 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.internal; | |
+ | |
+import com.google.inject.spi.OverrideBindingOption; | |
+ | |
+/** | |
+ * A processor to gather module options. | |
+ * | |
+ * @author jmnarloch@gmail.com (Jakub Narloch) | |
+ */ | |
+public class ModuleOptionsProcessor extends AbstractProcessor { | |
+ | |
+ private boolean overrideBindings = false; | |
+ | |
+ ModuleOptionsProcessor(Errors errors) { | |
+ super(errors); | |
+ } | |
+ | |
+ @Override | |
+ public Boolean visit(OverrideBindingOption option) { | |
+ overrideBindings = true; | |
+ return true; | |
+ } | |
+ | |
+ ModuleOptions getOptions() { | |
+ | |
+ return new ModuleOptions(overrideBindings); | |
+ } | |
+} | |
diff --git a/core/src/com/google/inject/internal/UntargettedBindingProcessor.java b/core/src/com/google/inject/internal/UntargettedBindingProcessor.java | |
index 8ca2ecc..fcee11d 100644 | |
--- a/core/src/com/google/inject/internal/UntargettedBindingProcessor.java | |
+++ b/core/src/com/google/inject/internal/UntargettedBindingProcessor.java | |
@@ -26,8 +26,8 @@ import com.google.inject.spi.UntargettedBinding; | |
*/ | |
class UntargettedBindingProcessor extends AbstractBindingProcessor { | |
- UntargettedBindingProcessor(Errors errors, ProcessedBindingData bindingData) { | |
- super(errors, bindingData); | |
+ UntargettedBindingProcessor(Errors errors, ProcessedBindingData bindingData, ModuleOptions moduleOptions) { | |
+ super(errors, bindingData, moduleOptions); | |
} | |
@Override | |
diff --git a/core/src/com/google/inject/spi/DefaultElementVisitor.java b/core/src/com/google/inject/spi/DefaultElementVisitor.java | |
index 0780bf8..13f4b4d 100644 | |
--- a/core/src/com/google/inject/spi/DefaultElementVisitor.java | |
+++ b/core/src/com/google/inject/spi/DefaultElementVisitor.java | |
@@ -102,4 +102,8 @@ public abstract class DefaultElementVisitor<V> implements ElementVisitor<V> { | |
public V visit(RequireExactBindingAnnotationsOption option) { | |
return visitOther(option); | |
} | |
+ | |
+ public V visit(OverrideBindingOption option) { | |
+ return visitOther(option); | |
+ } | |
} | |
diff --git a/core/src/com/google/inject/spi/ElementVisitor.java b/core/src/com/google/inject/spi/ElementVisitor.java | |
index 5e99086..de153bf 100644 | |
--- a/core/src/com/google/inject/spi/ElementVisitor.java | |
+++ b/core/src/com/google/inject/spi/ElementVisitor.java | |
@@ -120,4 +120,11 @@ public interface ElementVisitor<V> { | |
* @since 4.0 | |
*/ | |
V visit(RequireExactBindingAnnotationsOption option); | |
+ | |
+ /** | |
+ * Visit a override binding option. | |
+ * | |
+ * @since 4.0 | |
+ */ | |
+ V visit(OverrideBindingOption option); | |
} | |
diff --git a/core/src/com/google/inject/spi/Elements.java b/core/src/com/google/inject/spi/Elements.java | |
index 73999b3..0d9e829 100644 | |
--- a/core/src/com/google/inject/spi/Elements.java | |
+++ b/core/src/com/google/inject/spi/Elements.java | |
@@ -360,7 +360,11 @@ public final class Elements { | |
elements.add(new RequireExactBindingAnnotationsOption(getElementSource())); | |
} | |
- public void expose(Key<?> key) { | |
+ public void allowBindingOverride() { | |
+ elements.add(new OverrideBindingOption(getElementSource())); | |
+ } | |
+ | |
+ public void expose(Key<?> key) { | |
exposeInternal(key); | |
} | |
diff --git a/core/src/com/google/inject/spi/OverrideBindingOption.java b/core/src/com/google/inject/spi/OverrideBindingOption.java | |
new file mode 100644 | |
index 0000000..aa74141 | |
--- /dev/null | |
+++ b/core/src/com/google/inject/spi/OverrideBindingOption.java | |
@@ -0,0 +1,50 @@ | |
+/** | |
+ * Copyright (C) 2013 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 com.google.inject.Binder; | |
+ | |
+import static com.google.common.base.Preconditions.checkNotNull; | |
+ | |
+/** | |
+ * A setting for enabling binding override in the child injector. | |
+ * | |
+ * @author jmnarloch@gmail.com (Jakub Narloch) | |
+ * @since 4.0 | |
+ */ | |
+public class OverrideBindingOption implements Element { | |
+ private final Object source; | |
+ | |
+ OverrideBindingOption(Object source) { | |
+ this.source = checkNotNull(source, "source"); | |
+ } | |
+ | |
+ public Object getSource() { | |
+ if (source instanceof ElementSource) { | |
+ return ((ElementSource) source).getDeclaringSource(); | |
+ } | |
+ return source; | |
+ } | |
+ | |
+ public void applyTo(Binder binder) { | |
+ binder.withSource(getSource()).allowBindingOverride(); | |
+ } | |
+ | |
+ public <T> T acceptVisitor(ElementVisitor<T> visitor) { | |
+ return visitor.visit(this); | |
+ } | |
+} | |
diff --git a/core/test/com/google/inject/OverrideBindingTest.java b/core/test/com/google/inject/OverrideBindingTest.java | |
new file mode 100644 | |
index 0000000..21c6b7e | |
--- /dev/null | |
+++ b/core/test/com/google/inject/OverrideBindingTest.java | |
@@ -0,0 +1,123 @@ | |
+/** | |
+ * Copyright (C) 2013 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; | |
+ | |
+/** | |
+ * This tests verifies the whether the overriding the bindings works correctly. | |
+ * | |
+ * @author jmnarloch@gmail.com (Jakub Narloch) | |
+ */ | |
+public class OverrideBindingTest extends TestCase { | |
+ | |
+ /** | |
+ * Tests the simple injector creation. | |
+ */ | |
+ public void testNoBindingOverride() { | |
+ | |
+ Injector injector = Guice.createInjector(new BaseModule()); | |
+ | |
+ SimpleService instance = injector.getInstance(SimpleService.class); | |
+ assertEquals("Wrong type has been resolved.", BaseService.class, instance.getClass()); | |
+ } | |
+ | |
+ /** | |
+ * Tests the simple injector creation, when the child injector overrides the binding of the parent injector. | |
+ */ | |
+ public void testBindingOverride() { | |
+ | |
+ // creates the base injector | |
+ Injector baseInjector = Guice.createInjector(new BaseModule()); | |
+ | |
+ // creates new child injector with overridden binding | |
+ Injector injector = baseInjector.createChildInjector(new OverrideModule()); | |
+ | |
+ SimpleService instance = injector.getInstance(SimpleService.class); | |
+ assertEquals("Wrong type has been resolved.", MoreSpecificService.class, instance.getClass()); | |
+ } | |
+ | |
+ /** | |
+ * Tests the simple injector creation, when the child injector tries to overrides the binding of the parent | |
+ * injector, but does not turn configures the module to support it. | |
+ */ | |
+ public void testBindingOverrideDisabled() { | |
+ | |
+ // creates base injector | |
+ Injector baseInjector = Guice.createInjector(new BaseModule()); | |
+ | |
+ try { | |
+ // creates new injector that tries to override the binding, but did not enable that feature | |
+ Injector injector = baseInjector.createChildInjector(new OverrideModuleWithDisabledBindingOverride()); | |
+ fail("An CreationException was expected."); | |
+ } catch (CreationException ex) { | |
+ // expected exception | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * A simple module that is used for configuring the 'parent' injector. | |
+ */ | |
+ private static class BaseModule extends AbstractModule { | |
+ | |
+ @Override | |
+ protected void configure() { | |
+ binder().bind(SimpleService.class).to(BaseService.class); | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * A simple module that is used for configuring the child injector with overriding turing on. | |
+ */ | |
+ private static class OverrideModule extends AbstractModule { | |
+ | |
+ @Override | |
+ protected void configure() { | |
+ binder().allowBindingOverride(); | |
+ binder().bind(SimpleService.class).to(MoreSpecificService.class); | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * A simple module that is used for configuring the child injector and that tries to override the parent binding. | |
+ */ | |
+ private static class OverrideModuleWithDisabledBindingOverride extends AbstractModule { | |
+ | |
+ @Override | |
+ protected void configure() { | |
+ binder().bind(SimpleService.class).to(MoreSpecificService.class); | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * Imitates a some service layer interface. | |
+ */ | |
+ public static interface SimpleService { | |
+ } | |
+ | |
+ /** | |
+ * Imitates a some implementation of the {@link SimpleService}. | |
+ */ | |
+ public static class BaseService implements SimpleService { | |
+ } | |
+ | |
+ /** | |
+ * Imitates a some implementation of the {@link SimpleService}. | |
+ */ | |
+ public static class MoreSpecificService implements SimpleService { | |
+ } | |
+} | |
-- | |
1.7.11.msysgit.0 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment