Skip to content

Instantly share code, notes, and snippets.

@gissuebot
Created July 7, 2014 19:13
Show Gist options
  • Save gissuebot/29297c50e876ec28504f to your computer and use it in GitHub Desktop.
Save gissuebot/29297c50e876ec28504f to your computer and use it in GitHub Desktop.
Migrated attachment for Guice issue 770, comment 0
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