1 /** 2 * Copyright (C) 2008 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 */ 16 17 package com.google.inject.util; 18 19 import static com.google.inject.Asserts.asModuleChain; 20 import static com.google.inject.Asserts.assertContains; 21 import static com.google.inject.Guice.createInjector; 22 import static com.google.inject.name.Names.named; 23 import static java.lang.annotation.ElementType.METHOD; 24 import static java.lang.annotation.ElementType.TYPE; 25 import static java.lang.annotation.RetentionPolicy.RUNTIME; 26 27 import com.google.common.base.Objects; 28 import com.google.common.collect.ImmutableSet; 29 import com.google.inject.AbstractModule; 30 import com.google.inject.Binder; 31 import com.google.inject.Binding; 32 import com.google.inject.CreationException; 33 import com.google.inject.Exposed; 34 import com.google.inject.Guice; 35 import com.google.inject.Injector; 36 import com.google.inject.Key; 37 import com.google.inject.Module; 38 import com.google.inject.PrivateModule; 39 import com.google.inject.Provider; 40 import com.google.inject.Provides; 41 import com.google.inject.Scope; 42 import com.google.inject.ScopeAnnotation; 43 import com.google.inject.Stage; 44 import com.google.inject.name.Named; 45 import com.google.inject.name.Names; 46 import com.google.inject.spi.InjectionPoint; 47 import com.google.inject.spi.ModuleAnnotatedMethodScanner; 48 49 import junit.framework.TestCase; 50 51 import java.lang.annotation.Annotation; 52 import java.lang.annotation.Documented; 53 import java.lang.annotation.Retention; 54 import java.lang.annotation.Target; 55 import java.util.Date; 56 import java.util.Set; 57 import java.util.concurrent.atomic.AtomicReference; 58 59 /** 60 * @author sberlin@gmail.com (Sam Berlin) 61 */ 62 public class OverrideModuleTest extends TestCase { 63 64 private static final Key<String> key2 = Key.get(String.class, named("2")); 65 private static final Key<String> key3 = Key.get(String.class, named("3")); 66 67 private static final Module EMPTY_MODULE = new Module() { 68 public void configure(Binder binder) {} 69 }; 70 testOverride()71 public void testOverride() { 72 Injector injector = createInjector(Modules.override(newModule("A")).with(newModule("B"))); 73 assertEquals("B", injector.getInstance(String.class)); 74 } 75 testOverrideMultiple()76 public void testOverrideMultiple() { 77 Module module = Modules.override(newModule("A"), newModule(1), newModule(0.5f)) 78 .with(newModule("B"), newModule(2), newModule(1.5d)); 79 Injector injector = createInjector(module); 80 assertEquals("B", injector.getInstance(String.class)); 81 assertEquals(2, injector.getInstance(Integer.class).intValue()); 82 assertEquals(0.5f, injector.getInstance(Float.class)); 83 assertEquals(1.5d, injector.getInstance(Double.class)); 84 } 85 testOverrideUnmatchedTolerated()86 public void testOverrideUnmatchedTolerated() { 87 Injector injector = createInjector(Modules.override(EMPTY_MODULE).with(newModule("B"))); 88 assertEquals("B", injector.getInstance(String.class)); 89 } 90 testOverrideConstant()91 public void testOverrideConstant() { 92 Module original = new AbstractModule() { 93 @Override protected void configure() { 94 bindConstant().annotatedWith(named("Test")).to("A"); 95 } 96 }; 97 98 Module replacements = new AbstractModule() { 99 @Override protected void configure() { 100 bindConstant().annotatedWith(named("Test")).to("B"); 101 } 102 }; 103 104 Injector injector = createInjector(Modules.override(original).with(replacements)); 105 assertEquals("B", injector.getInstance(Key.get(String.class, named("Test")))); 106 } 107 testGetProviderInModule()108 public void testGetProviderInModule() { 109 Module original = new AbstractModule() { 110 @Override protected void configure() { 111 bind(String.class).toInstance("A"); 112 bind(key2).toProvider(getProvider(String.class)); 113 } 114 }; 115 116 Injector injector = createInjector(Modules.override(original).with(EMPTY_MODULE)); 117 assertEquals("A", injector.getInstance(String.class)); 118 assertEquals("A", injector.getInstance(key2)); 119 } 120 testOverrideWhatGetProviderProvided()121 public void testOverrideWhatGetProviderProvided() { 122 Module original = new AbstractModule() { 123 @Override protected void configure() { 124 bind(String.class).toInstance("A"); 125 bind(key2).toProvider(getProvider(String.class)); 126 } 127 }; 128 129 Module replacements = newModule("B"); 130 131 Injector injector = createInjector(Modules.override(original).with(replacements)); 132 assertEquals("B", injector.getInstance(String.class)); 133 assertEquals("B", injector.getInstance(key2)); 134 } 135 testOverrideUsingOriginalsGetProvider()136 public void testOverrideUsingOriginalsGetProvider() { 137 Module original = new AbstractModule() { 138 @Override protected void configure() { 139 bind(String.class).toInstance("A"); 140 bind(key2).toInstance("B"); 141 } 142 }; 143 144 Module replacements = new AbstractModule() { 145 @Override protected void configure() { 146 bind(String.class).toProvider(getProvider(key2)); 147 } 148 }; 149 150 Injector injector = createInjector(Modules.override(original).with(replacements)); 151 assertEquals("B", injector.getInstance(String.class)); 152 assertEquals("B", injector.getInstance(key2)); 153 } 154 testOverrideOfOverride()155 public void testOverrideOfOverride() { 156 Module original = new AbstractModule() { 157 @Override protected void configure() { 158 bind(String.class).toInstance("A1"); 159 bind(key2).toInstance("A2"); 160 bind(key3).toInstance("A3"); 161 } 162 }; 163 164 Module replacements1 = new AbstractModule() { 165 @Override protected void configure() { 166 bind(String.class).toInstance("B1"); 167 bind(key2).toInstance("B2"); 168 } 169 }; 170 171 Module overrides = Modules.override(original).with(replacements1); 172 173 Module replacements2 = new AbstractModule() { 174 @Override protected void configure() { 175 bind(String.class).toInstance("C1"); 176 bind(key3).toInstance("C3"); 177 } 178 }; 179 180 Injector injector = createInjector(Modules.override(overrides).with(replacements2)); 181 assertEquals("C1", injector.getInstance(String.class)); 182 assertEquals("B2", injector.getInstance(key2)); 183 assertEquals("C3", injector.getInstance(key3)); 184 } 185 186 static class OuterReplacementsModule extends AbstractModule { configure()187 @Override protected void configure() { 188 install(new InnerReplacementsModule()); 189 } 190 } 191 static class InnerReplacementsModule extends AbstractModule { configure()192 @Override protected void configure() { 193 bind(String.class).toInstance("B"); 194 bind(String.class).toInstance("C"); 195 } 196 } testOverridesTwiceFails()197 public void testOverridesTwiceFails() { 198 Module original = newModule("A"); 199 Module replacements = new OuterReplacementsModule(); 200 Module module = Modules.override(original).with(replacements); 201 try { 202 createInjector(module); 203 fail(); 204 } catch (CreationException expected) { 205 assertContains(expected.getMessage(), 206 "A binding to java.lang.String was already configured at " 207 + InnerReplacementsModule.class.getName(), 208 asModuleChain(Modules.OverrideModule.class, 209 OuterReplacementsModule.class, InnerReplacementsModule.class), 210 "at " + InnerReplacementsModule.class.getName(), 211 asModuleChain(Modules.OverrideModule.class, 212 OuterReplacementsModule.class, InnerReplacementsModule.class)); 213 } 214 } 215 testOverridesDoesntFixTwiceBoundInOriginal()216 public void testOverridesDoesntFixTwiceBoundInOriginal() { 217 Module original = new AbstractModule() { 218 @Override protected void configure() { 219 bind(String.class).toInstance("A"); 220 bind(String.class).toInstance("B"); 221 } 222 }; 223 224 Module replacements = new AbstractModule() { 225 @Override protected void configure() { 226 bind(String.class).toInstance("C"); 227 } 228 }; 229 230 Module module = Modules.override(original).with(replacements); 231 try { 232 createInjector(module); 233 fail(); 234 } catch (CreationException expected) { 235 // The replacement comes first because we replace A with C, 236 // then we encounter B and freak out. 237 assertContains(expected.getMessage(), 238 "1) A binding to java.lang.String was already configured at " 239 + replacements.getClass().getName(), 240 asModuleChain(Modules.OverrideModule.class, replacements.getClass()), 241 "at " + original.getClass().getName(), 242 asModuleChain(Modules.OverrideModule.class, original.getClass())); 243 } 244 } 245 testStandardScopeAnnotation()246 public void testStandardScopeAnnotation() { 247 final SingleUseScope scope = new SingleUseScope(); 248 249 Module module = new AbstractModule() { 250 @Override protected void configure() { 251 bindScope(TestScopeAnnotation.class, scope); 252 bind(String.class).in(TestScopeAnnotation.class); 253 } 254 }; 255 assertFalse(scope.used); 256 257 Guice.createInjector(module); 258 assertTrue(scope.used); 259 } 260 testOverrideUntargettedBinding()261 public void testOverrideUntargettedBinding() { 262 Module original = new AbstractModule() { 263 @Override protected void configure() { 264 bind(Date.class); 265 } 266 }; 267 268 Module replacements = new AbstractModule() { 269 @Override protected void configure() { 270 bind(Date.class).toInstance(new Date(0)); 271 } 272 }; 273 274 Injector injector = createInjector(Modules.override(original).with(replacements)); 275 assertEquals(0, injector.getInstance(Date.class).getTime()); 276 } 277 testOverrideScopeAnnotation()278 public void testOverrideScopeAnnotation() { 279 final Scope scope = new Scope() { 280 public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) { 281 throw new AssertionError("Should not be called"); 282 } 283 }; 284 285 final SingleUseScope replacementScope = new SingleUseScope(); 286 287 Module original = new AbstractModule() { 288 @Override protected void configure() { 289 bindScope(TestScopeAnnotation.class, scope); 290 bind(Date.class).in(TestScopeAnnotation.class); 291 } 292 }; 293 294 Module replacements = new AbstractModule() { 295 @Override protected void configure() { 296 bindScope(TestScopeAnnotation.class, replacementScope); 297 } 298 }; 299 300 Injector injector = createInjector(Modules.override(original).with(replacements)); 301 injector.getInstance(Date.class); 302 assertTrue(replacementScope.used); 303 } 304 testFailsIfOverridenScopeInstanceHasBeenUsed()305 public void testFailsIfOverridenScopeInstanceHasBeenUsed() { 306 final Scope scope = new Scope() { 307 public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) { 308 return unscoped; 309 } 310 311 @Override public String toString() { 312 return "ORIGINAL SCOPE"; 313 } 314 }; 315 316 final Module original = new AbstractModule() { 317 @Override protected void configure() { 318 bindScope(TestScopeAnnotation.class, scope); 319 bind(Date.class).in(scope); 320 bind(String.class).in(scope); 321 } 322 }; 323 Module originalWrapper = new AbstractModule() { 324 @Override protected void configure() { 325 install(original); 326 } 327 }; 328 329 Module replacements = new AbstractModule() { 330 @Override protected void configure() { 331 bindScope(TestScopeAnnotation.class, new SingleUseScope()); 332 } 333 }; 334 335 try { 336 createInjector(Modules.override(originalWrapper).with(replacements)); 337 fail("Exception expected"); 338 } catch (CreationException e) { 339 assertContains(e.getMessage(), 340 "1) The scope for @TestScopeAnnotation is bound directly and cannot be overridden.", 341 "original binding at " + original.getClass().getName() + ".configure(", 342 asModuleChain(originalWrapper.getClass(), original.getClass()), 343 "bound directly at " + original.getClass().getName() + ".configure(", 344 asModuleChain(originalWrapper.getClass(), original.getClass()), 345 "bound directly at " + original.getClass().getName() + ".configure(", 346 asModuleChain(originalWrapper.getClass(), original.getClass()), 347 "at ", replacements.getClass().getName() + ".configure(", 348 asModuleChain(Modules.OverrideModule.class, replacements.getClass())); 349 } 350 } 351 testOverrideIsLazy()352 public void testOverrideIsLazy() { 353 final AtomicReference<String> value = new AtomicReference<String>("A"); 354 Module overridden = Modules.override(new AbstractModule() { 355 @Override protected void configure() { 356 bind(String.class).annotatedWith(named("original")).toInstance(value.get()); 357 } 358 }).with(new AbstractModule() { 359 @Override protected void configure() { 360 bind(String.class).annotatedWith(named("override")).toInstance(value.get()); 361 } 362 }); 363 364 // the value.get() call should be deferred until Guice.createInjector 365 value.set("B"); 366 Injector injector = Guice.createInjector(overridden); 367 assertEquals("B", injector.getInstance(Key.get(String.class, named("original")))); 368 assertEquals("B", injector.getInstance(Key.get(String.class, named("override")))); 369 } 370 testOverridePrivateModuleOverPrivateModule()371 public void testOverridePrivateModuleOverPrivateModule() { 372 Module exposes5and6 = new AbstractModule() { 373 @Override protected void configure() { 374 install(new PrivateModule() { 375 @Override protected void configure() { 376 bind(Integer.class).toInstance(5); 377 expose(Integer.class); 378 379 bind(Character.class).toInstance('E'); 380 } 381 }); 382 383 install(new PrivateModule() { 384 @Override protected void configure() { 385 bind(Long.class).toInstance(6L); 386 expose(Long.class); 387 388 bind(Character.class).toInstance('F'); 389 } 390 }); 391 } 392 }; 393 394 AbstractModule exposes15 = new AbstractModule() { 395 @Override protected void configure() { 396 install(new PrivateModule() { 397 @Override protected void configure() { 398 bind(Integer.class).toInstance(15); 399 expose(Integer.class); 400 401 bind(Character.class).toInstance('G'); 402 } 403 }); 404 405 install(new PrivateModule() { 406 @Override protected void configure() { 407 bind(Character.class).toInstance('H'); 408 } 409 }); 410 } 411 }; 412 413 // override forwards 414 Injector injector = Guice.createInjector(Modules.override(exposes5and6).with(exposes15)); 415 assertEquals(15, injector.getInstance(Integer.class).intValue()); 416 assertEquals(6L, injector.getInstance(Long.class).longValue()); 417 418 // and in reverse order 419 Injector reverse = Guice.createInjector(Modules.override(exposes15).with(exposes5and6)); 420 assertEquals(5, reverse.getInstance(Integer.class).intValue()); 421 assertEquals(6L, reverse.getInstance(Long.class).longValue()); 422 } 423 testOverrideModuleAndPrivateModule()424 public void testOverrideModuleAndPrivateModule() { 425 Module exposes5 = new PrivateModule() { 426 @Override protected void configure() { 427 bind(Integer.class).toInstance(5); 428 expose(Integer.class); 429 } 430 }; 431 432 Module binds15 = new AbstractModule() { 433 @Override protected void configure() { 434 bind(Integer.class).toInstance(15); 435 } 436 }; 437 438 Injector injector = Guice.createInjector(Modules.override(exposes5).with(binds15)); 439 assertEquals(15, injector.getInstance(Integer.class).intValue()); 440 441 Injector reverse = Guice.createInjector(Modules.override(binds15).with(exposes5)); 442 assertEquals(5, reverse.getInstance(Integer.class).intValue()); 443 } 444 testOverrideDeepExpose()445 public void testOverrideDeepExpose() { 446 final AtomicReference<Provider<Character>> charAProvider 447 = new AtomicReference<Provider<Character>>(); 448 449 Module exposes5 = new PrivateModule() { 450 @Override protected void configure() { 451 install(new PrivateModule() { 452 @Override protected void configure() { 453 bind(Integer.class).toInstance(5); 454 expose(Integer.class); 455 charAProvider.set(getProvider(Character.class)); 456 bind(Character.class).toInstance('A'); 457 } 458 }); 459 expose(Integer.class); 460 } 461 }; 462 463 Injector injector = Guice.createInjector(Modules.override(exposes5).with(EMPTY_MODULE)); 464 assertEquals(5, injector.getInstance(Integer.class).intValue()); 465 assertEquals('A', charAProvider.getAndSet(null).get().charValue()); 466 467 injector = Guice.createInjector(Modules.override(EMPTY_MODULE).with(exposes5)); 468 assertEquals(5, injector.getInstance(Integer.class).intValue()); 469 assertEquals('A', charAProvider.getAndSet(null).get().charValue()); 470 471 final AtomicReference<Provider<Character>> charBProvider 472 = new AtomicReference<Provider<Character>>(); 473 474 Module binds15 = new AbstractModule() { 475 @Override protected void configure() { 476 bind(Integer.class).toInstance(15); 477 478 install(new PrivateModule() { 479 @Override protected void configure() { 480 charBProvider.set(getProvider(Character.class)); 481 bind(Character.class).toInstance('B'); 482 } 483 }); 484 } 485 }; 486 487 injector = Guice.createInjector(Modules.override(binds15).with(exposes5)); 488 assertEquals(5, injector.getInstance(Integer.class).intValue()); 489 assertEquals('A', charAProvider.getAndSet(null).get().charValue()); 490 assertEquals('B', charBProvider.getAndSet(null).get().charValue()); 491 492 injector = Guice.createInjector(Modules.override(exposes5).with(binds15)); 493 assertEquals(15, injector.getInstance(Integer.class).intValue()); 494 assertEquals('A', charAProvider.getAndSet(null).get().charValue()); 495 assertEquals('B', charBProvider.getAndSet(null).get().charValue()); 496 } 497 498 @Retention(RUNTIME) 499 @Target(TYPE) 500 @ScopeAnnotation 501 private static @interface TestScopeAnnotation {} 502 503 private static class SingleUseScope implements Scope { 504 boolean used = false; scope(Key<T> key, Provider<T> unscoped)505 public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) { 506 assertFalse(used); 507 used = true; 508 return unscoped; 509 } 510 } 511 512 static class NewModule<T> extends AbstractModule { 513 private final T bound; NewModule(T bound)514 NewModule(T bound) { 515 this.bound = bound; 516 } configure()517 @Override protected void configure() { 518 @SuppressWarnings("unchecked") 519 Class<T> type = (Class<T>)bound.getClass(); 520 bind(type).toInstance(bound); 521 } 522 } 523 newModule(final T bound)524 private static <T> Module newModule(final T bound) { 525 return new NewModule<T>(bound); 526 } 527 528 private static final String RESULT = "RESULT"; 529 private static final String PRIVATE_INPUT = "PRIVATE_INPUT"; 530 private static final String OVERRIDDEN_INPUT = "FOO"; 531 private static final String OVERRIDDEN_RESULT = "Size: 3"; 532 private static final Key<String> RESULT_KEY = Key.get(String.class, named(RESULT)); 533 private static final Key<String> INPUT_KEY = Key.get(String.class, named(PRIVATE_INPUT)); 534 testExposedBindingOverride()535 public void testExposedBindingOverride() throws Exception { 536 Injector inj = Guice.createInjector( 537 Modules.override(new ExampleModule()).with( 538 new AbstractModule() { 539 @Override protected void configure() { 540 bind(RESULT_KEY).toInstance(OVERRIDDEN_RESULT); 541 } 542 })); 543 assertEquals(inj.getInstance(RESULT_KEY), OVERRIDDEN_RESULT); 544 } 545 testPrivateBindingOverride()546 public void testPrivateBindingOverride() throws Exception { 547 Injector inj = Guice.createInjector( 548 Modules.override(new ExampleModule()).with( 549 new AbstractModule() { 550 @Override protected void configure() { 551 bind(INPUT_KEY).toInstance(OVERRIDDEN_INPUT); 552 } 553 })); 554 assertEquals(inj.getInstance(RESULT_KEY), OVERRIDDEN_RESULT); 555 } 556 557 public static class ExampleModule extends PrivateModule { 558 @Provides @Exposed @Named(RESULT) provideResult(@amedPRIVATE_INPUT) String input)559 public String provideResult(@Named(PRIVATE_INPUT) String input) { 560 return "Size: " + input.length(); 561 } 562 563 @Provides @Named(PRIVATE_INPUT) provideInput()564 public String provideInput() { 565 return "Hello World"; 566 } 567 configure()568 @Override protected void configure() { 569 } 570 } 571 testEqualsNotCalledByDefaultOnInstance()572 public void testEqualsNotCalledByDefaultOnInstance() { 573 final HashEqualsTester a = new HashEqualsTester(); 574 a.throwOnEquals = true; 575 Guice.createInjector(Modules.override(new AbstractModule() { 576 @Override 577 protected void configure() { 578 bind(String.class); 579 bind(HashEqualsTester.class).toInstance(a); 580 } 581 }).with()); 582 } 583 testEqualsNotCalledByDefaultOnProvider()584 public void testEqualsNotCalledByDefaultOnProvider() { 585 final HashEqualsTester a = new HashEqualsTester(); 586 a.throwOnEquals = true; 587 Guice.createInjector(Modules.override(new AbstractModule() { 588 @Override 589 protected void configure() { 590 bind(String.class); 591 bind(Object.class).toProvider(a); 592 } 593 }).with()); 594 } 595 testHashcodeNeverCalledOnInstance()596 public void testHashcodeNeverCalledOnInstance() { 597 final HashEqualsTester a = new HashEqualsTester(); 598 a.throwOnHashcode = true; 599 a.equality = "test"; 600 601 final HashEqualsTester b = new HashEqualsTester(); 602 b.throwOnHashcode = true; 603 b.equality = "test"; 604 Guice.createInjector(Modules.override(new AbstractModule() { 605 @Override 606 protected void configure() { 607 bind(String.class); 608 bind(HashEqualsTester.class).toInstance(a); 609 bind(HashEqualsTester.class).toInstance(b); 610 } 611 }).with()); 612 } 613 testHashcodeNeverCalledOnProviderInstance()614 public void testHashcodeNeverCalledOnProviderInstance() { 615 final HashEqualsTester a = new HashEqualsTester(); 616 a.throwOnHashcode = true; 617 a.equality = "test"; 618 619 final HashEqualsTester b = new HashEqualsTester(); 620 b.throwOnHashcode = true; 621 b.equality = "test"; 622 Guice.createInjector(Modules.override(new AbstractModule() { 623 @Override 624 protected void configure() { 625 bind(String.class); 626 bind(Object.class).toProvider(a); 627 bind(Object.class).toProvider(b); 628 } 629 }).with()); 630 } 631 632 private static class HashEqualsTester implements Provider<Object> { 633 private String equality; 634 private boolean throwOnEquals; 635 private boolean throwOnHashcode; 636 637 @Override equals(Object obj)638 public boolean equals(Object obj) { 639 if (throwOnEquals) { 640 throw new RuntimeException(); 641 } else if (obj instanceof HashEqualsTester) { 642 HashEqualsTester o = (HashEqualsTester)obj; 643 if(o.throwOnEquals) { 644 throw new RuntimeException(); 645 } 646 if(equality == null && o.equality == null) { 647 return this == o; 648 } else { 649 return Objects.equal(equality, o.equality); 650 } 651 } else { 652 return false; 653 } 654 } 655 656 @Override hashCode()657 public int hashCode() { 658 if(throwOnHashcode) { 659 throw new RuntimeException(); 660 } else { 661 return super.hashCode(); 662 } 663 } 664 get()665 public Object get() { 666 return new Object(); 667 } 668 } 669 testCorrectStage()670 public void testCorrectStage() { 671 final Stage stage = Stage.PRODUCTION; 672 Module module = Modules.override(new AbstractModule() { 673 @Override 674 protected void configure() { 675 if (currentStage() != Stage.PRODUCTION) { 676 addError("Wronge stage in overridden module:" + currentStage()); 677 } 678 } 679 }).with(new AbstractModule() { 680 @Override 681 protected void configure() { 682 if (currentStage() != Stage.PRODUCTION) { 683 addError("Wronge stage in overriding module:" + currentStage()); 684 } 685 } 686 }); 687 Guice.createInjector(stage, module); 688 } 689 testOverridesApplyOriginalScanners()690 public void testOverridesApplyOriginalScanners() { 691 Injector injector = 692 Guice.createInjector(Modules.override(NamedMunger.module()).with(new AbstractModule() { 693 @Override protected void configure() {} 694 @TestProvides @Named("test") String provideString() { return "foo"; } 695 })); 696 697 assertNull(injector.getExistingBinding(Key.get(String.class, named("test")))); 698 Binding<String> binding = injector.getBinding(Key.get(String.class, named("test-munged"))); 699 assertEquals("foo", binding.getProvider().get()); 700 } 701 702 @Documented @Target(METHOD) @Retention(RUNTIME) 703 private @interface TestProvides {} 704 705 private static class NamedMunger extends ModuleAnnotatedMethodScanner { module()706 static Module module() { 707 return new AbstractModule() { 708 @Override protected void configure() { 709 binder().scanModulesForAnnotatedMethods(new NamedMunger()); 710 } 711 }; 712 } 713 714 @Override toString()715 public String toString() { 716 return "NamedMunger"; 717 } 718 719 @Override annotationClasses()720 public Set<? extends Class<? extends Annotation>> annotationClasses() { 721 return ImmutableSet.of(TestProvides.class); 722 } 723 724 @Override prepareMethod(Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint)725 public <T> Key<T> prepareMethod(Binder binder, Annotation annotation, Key<T> key, 726 InjectionPoint injectionPoint) { 727 return Key.get(key.getTypeLiteral(), 728 Names.named(((Named) key.getAnnotation()).value() + "-munged")); 729 } 730 } 731 } 732