| /** |
| * Copyright (C) 2011 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 static com.google.common.collect.ImmutableList.of; |
| import static com.google.inject.Asserts.assertContains; |
| import static com.google.inject.name.Names.named; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Lists; |
| import com.google.inject.matcher.AbstractMatcher; |
| import com.google.inject.matcher.Matcher; |
| import com.google.inject.matcher.Matchers; |
| import com.google.inject.name.Named; |
| import com.google.inject.spi.DependencyAndSource; |
| import com.google.inject.spi.InstanceBinding; |
| import com.google.inject.spi.ProvisionListener; |
| import com.google.inject.util.Providers; |
| |
| import junit.framework.TestCase; |
| |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| /** |
| * Tests for {@link Binder#bindListener(Matcher, ProvisionListener...)} |
| * |
| * @author sameb@google.com (Sam Berlin) |
| */ |
| // TODO(sameb): Add some tests for private modules & child injectors. |
| public class ProvisionListenerTest extends TestCase { |
| |
| public void testExceptionInListenerBeforeProvisioning() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), new FailBeforeProvision()); |
| } |
| }); |
| try { |
| injector.getInstance(Foo.class); |
| fail(); |
| } catch(ProvisionException pe) { |
| assertEquals(1, pe.getErrorMessages().size()); |
| assertContains(pe.getMessage(), |
| "1) Error notifying ProvisionListener " + FailBeforeProvision.class.getName() |
| + " of " + Foo.class.getName(), |
| "Reason: java.lang.RuntimeException: boo", |
| "while locating " + Foo.class.getName()); |
| assertEquals("boo", pe.getCause().getMessage()); |
| } |
| } |
| |
| public void testExceptionInListenerAfterProvisioning() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), new FailAfterProvision()); |
| } |
| }); |
| try { |
| injector.getInstance(Foo.class); |
| fail(); |
| } catch(ProvisionException pe) { |
| assertEquals(1, pe.getErrorMessages().size()); |
| assertContains(pe.getMessage(), |
| "1) Error notifying ProvisionListener " + FailAfterProvision.class.getName() |
| + " of " + Foo.class.getName(), |
| "Reason: java.lang.RuntimeException: boo", |
| "while locating " + Foo.class.getName()); |
| assertEquals("boo", pe.getCause().getMessage()); |
| } |
| } |
| |
| public void testExceptionInProvisionExplicitlyCalled() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), new JustProvision()); |
| } |
| }); |
| try { |
| injector.getInstance(FooBomb.class); |
| fail(); |
| } catch(ProvisionException pe) { |
| assertEquals(1, pe.getErrorMessages().size()); |
| assertContains(pe.getMessage(), |
| "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail", |
| " at " + FooBomb.class.getName(), |
| " while locating " + FooBomb.class.getName()); |
| assertEquals("Retry, Abort, Fail", pe.getCause().getMessage()); |
| } |
| } |
| |
| public void testExceptionInProvisionAutomaticallyCalled() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), new NoProvision()); |
| } |
| }); |
| try { |
| injector.getInstance(FooBomb.class); |
| fail(); |
| } catch(ProvisionException pe) { |
| assertEquals(1, pe.getErrorMessages().size()); |
| assertContains(pe.getMessage(), |
| "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail", |
| " at " + FooBomb.class.getName(), |
| " while locating " + FooBomb.class.getName()); |
| assertEquals("Retry, Abort, Fail", pe.getCause().getMessage()); |
| } |
| } |
| |
| public void testListenerCallsProvisionTwice() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), new ProvisionTwice()); |
| } |
| }); |
| try { |
| injector.getInstance(Foo.class); |
| fail(); |
| } catch(ProvisionException pe) { |
| assertEquals(1, pe.getErrorMessages().size()); |
| assertContains(pe.getMessage(), |
| "1) Error notifying ProvisionListener " + ProvisionTwice.class.getName() |
| + " of " + Foo.class.getName(), |
| "Reason: java.lang.IllegalStateException: Already provisioned in this listener.", |
| "while locating " + Foo.class.getName()); |
| assertEquals("Already provisioned in this listener.", pe.getCause().getMessage()); |
| } |
| } |
| |
| public void testCachedInScopePreventsProvisionNotify() { |
| final Counter count1 = new Counter(); |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), count1); |
| bind(Foo.class).in(Scopes.SINGLETON); |
| } |
| }); |
| Foo foo = injector.getInstance(Foo.class); |
| assertNotNull(foo); |
| assertEquals(1, count1.count); |
| |
| // not notified the second time because nothing is provisioned |
| // (it's cached in the scope) |
| count1.count = 0; |
| assertSame(foo, injector.getInstance(Foo.class)); |
| assertEquals(0, count1.count); |
| } |
| |
| public void testCombineAllBindListenerCalls() { |
| final Counter count1 = new Counter(); |
| final Counter count2 = new Counter(); |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), count1); |
| bindListener(Matchers.any(), count2); |
| } |
| }); |
| assertNotNull(injector.getInstance(Foo.class)); |
| assertEquals(1, count1.count); |
| assertEquals(1, count2.count); |
| } |
| |
| public void testNotifyEarlyListenersIfFailBeforeProvision() { |
| final Counter count1 = new Counter(); |
| final Counter count2 = new Counter(); |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), count1, new FailBeforeProvision(), count2); |
| } |
| }); |
| try { |
| injector.getInstance(Foo.class); |
| fail(); |
| } catch(ProvisionException pe) { |
| assertEquals(1, pe.getErrorMessages().size()); |
| assertContains(pe.getMessage(), |
| "1) Error notifying ProvisionListener " + FailBeforeProvision.class.getName() |
| + " of " + Foo.class.getName(), |
| "Reason: java.lang.RuntimeException: boo", |
| "while locating " + Foo.class.getName()); |
| assertEquals("boo", pe.getCause().getMessage()); |
| |
| assertEquals(1, count1.count); |
| assertEquals(0, count2.count); |
| } |
| } |
| |
| public void testNotifyLaterListenersIfFailAfterProvision() { |
| final Counter count1 = new Counter(); |
| final Counter count2 = new Counter(); |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), count1, new FailAfterProvision(), count2); |
| } |
| }); |
| try { |
| injector.getInstance(Foo.class); |
| fail(); |
| } catch(ProvisionException pe) { |
| assertEquals(1, pe.getErrorMessages().size()); |
| assertContains(pe.getMessage(), |
| "1) Error notifying ProvisionListener " + FailAfterProvision.class.getName() |
| + " of " + Foo.class.getName(), |
| "Reason: java.lang.RuntimeException: boo", |
| "while locating " + Foo.class.getName()); |
| assertEquals("boo", pe.getCause().getMessage()); |
| |
| assertEquals(1, count1.count); |
| assertEquals(1, count2.count); |
| } |
| } |
| |
| public void testNotifiedKeysOfAllBindTypes() { |
| final Capturer capturer = new Capturer(); |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), capturer); |
| bind(Foo.class).annotatedWith(named("pk")).toProvider(FooP.class); |
| try { |
| bind(Foo.class).annotatedWith(named("cxtr")).toConstructor(Foo.class.getDeclaredConstructor()); |
| } catch (Exception ex) { |
| throw new RuntimeException(ex); |
| } |
| bind(LinkedFoo.class).to(Foo.class); |
| bind(Interface.class).toInstance(new Implementation()); |
| bindConstant().annotatedWith(named("constant")).to("MyConstant"); |
| } |
| |
| @Provides @Named("pi") Foo provideFooBar() { |
| return new Foo(); |
| } |
| }); |
| |
| // toInstance & constant bindings are notified in random order, at the very beginning. |
| assertEquals( |
| ImmutableSet.of(Key.get(Interface.class), Key.get(String.class, named("constant"))), |
| capturer.getAsSetAndClear()); |
| |
| // simple binding |
| assertNotNull(injector.getInstance(Foo.class)); |
| assertEquals(of(Key.get(Foo.class)), capturer.getAndClear()); |
| |
| // provider key binding -- notifies about provider & the object, always |
| assertNotNull(injector.getInstance(Key.get(Foo.class, named("pk")))); |
| assertEquals(of(Key.get(FooP.class), Key.get(Foo.class, named("pk"))), capturer.getAndClear()); |
| assertNotNull(injector.getInstance(Key.get(Foo.class, named("pk")))); |
| assertEquals(of(Key.get(FooP.class), Key.get(Foo.class, named("pk"))), capturer.getAndClear()); |
| |
| // JIT provider key binding -- notifies about provider & the object, always |
| assertNotNull(injector.getInstance(JitFoo2.class)); |
| assertEquals(of(Key.get(JitFoo2P.class), Key.get(JitFoo2.class)), capturer.getAndClear()); |
| assertNotNull(injector.getInstance(JitFoo2.class)); |
| assertEquals(of(Key.get(JitFoo2P.class), Key.get(JitFoo2.class)), capturer.getAndClear()); |
| |
| // provider instance binding -- just the object (not the provider) |
| assertNotNull(injector.getInstance(Key.get(Foo.class, named("pi")))); |
| assertEquals(of(Key.get(Foo.class, named("pi"))), capturer.getAndClear()); |
| |
| // toConstructor binding |
| assertNotNull(injector.getInstance(Key.get(Foo.class, named("cxtr")))); |
| assertEquals(of(Key.get(Foo.class, named("cxtr"))), capturer.getAndClear()); |
| |
| // linked binding -- notifies about the target (that's what's provisioned), not the link |
| assertNotNull(injector.getInstance(LinkedFoo.class)); |
| assertEquals(of(Key.get(Foo.class)), capturer.getAndClear()); |
| |
| // JIT linked binding -- notifies about the target (that's what's provisioned), not the link |
| assertNotNull(injector.getInstance(JitFoo.class)); |
| assertEquals(of(Key.get(Foo.class)), capturer.getAndClear()); |
| } |
| |
| public void testSingletonMatcher() { |
| final Counter counter = new Counter(); |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(new AbstractMatcher<Binding<?>>() { |
| @Override |
| public boolean matches(Binding<?> t) { |
| return Scopes.isSingleton(t); |
| } |
| }, counter); |
| } |
| }); |
| assertEquals(0, counter.count); |
| // no increment for getting Many. |
| injector.getInstance(Many.class); |
| assertEquals(0, counter.count); |
| // but an increment for getting Sole, since it's a singleton. |
| injector.getInstance(Sole.class); |
| assertEquals(1, counter.count); |
| } |
| |
| public void testCallingBindingDotGetProviderDotGet() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), new ProvisionListener() { |
| @Override |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| provision.getBinding().getProvider().get(); // AGH! |
| } |
| }); |
| } |
| }); |
| |
| try { |
| injector.getInstance(Sole.class); |
| fail(); |
| } catch(ProvisionException expected) { |
| // We don't really care what kind of error you get, we only care you get an error. |
| } |
| |
| try { |
| injector.getInstance(Many.class); |
| fail(); |
| } catch(ProvisionException expected) { |
| // We don't really care what kind of error you get, we only care you get an error. |
| } |
| } |
| |
| interface Interface {} |
| class Implementation implements Interface {} |
| |
| @Singleton static class Sole {} |
| static class Many {} |
| |
| @ImplementedBy(Foo.class) static interface JitFoo {} |
| @ProvidedBy(JitFoo2P.class) static class JitFoo2 {} |
| static interface LinkedFoo {} |
| static class Foo implements JitFoo, LinkedFoo {} |
| static class FooP implements Provider<Foo> { |
| public Foo get() { |
| return new Foo(); |
| } |
| } |
| static class JitFoo2P implements Provider<JitFoo2> { |
| public JitFoo2 get() { |
| return new JitFoo2(); |
| } |
| } |
| |
| static class FooBomb { |
| FooBomb() { |
| throw new RuntimeException("Retry, Abort, Fail"); |
| } |
| } |
| |
| private static class Counter implements ProvisionListener { |
| int count = 0; |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| count++; |
| } |
| } |
| |
| private static class Capturer implements ProvisionListener { |
| List<Key> keys = Lists.newArrayList(); |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| keys.add(provision.getBinding().getKey()); |
| T provisioned = provision.provision(); |
| // InstanceBindings are the only kind of binding where the key can |
| // be an instanceof the provisioned, because it isn't linked to any |
| // direct implementation. I guess maybe it'd also be possible |
| // with a toConstructor binding... but we don't use that in our tests. |
| if (provision.getBinding() instanceof InstanceBinding) { |
| Class<? super T> expected = provision.getBinding().getKey().getRawType(); |
| assertTrue("expected instanceof: " + expected + ", but was: " + provisioned, |
| expected.isInstance(provisioned)); |
| } else { |
| assertEquals(provision.getBinding().getKey().getRawType(), provisioned.getClass()); |
| } |
| } |
| |
| Set<Key> getAsSetAndClear() { |
| Set<Key> copy = ImmutableSet.copyOf(keys); |
| keys.clear(); |
| return copy; |
| } |
| |
| List<Key> getAndClear() { |
| List<Key> copy = ImmutableList.copyOf(keys); |
| keys.clear(); |
| return copy; |
| } |
| } |
| |
| private static class FailBeforeProvision implements ProvisionListener { |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| throw new RuntimeException("boo"); |
| } |
| } |
| private static class FailAfterProvision implements ProvisionListener { |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| provision.provision(); |
| throw new RuntimeException("boo"); |
| } |
| } |
| private static class JustProvision implements ProvisionListener { |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| provision.provision(); |
| } |
| } |
| private static class NoProvision implements ProvisionListener { |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| } |
| } |
| private static class ProvisionTwice implements ProvisionListener { |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| provision.provision(); |
| provision.provision(); |
| } |
| } |
| |
| private static class ChainAsserter implements ProvisionListener { |
| private final List<Class<?>> provisionList; |
| private final List<Class<?>> expected; |
| |
| public ChainAsserter(List<Class<?>> provisionList, Iterable<Class<?>> expected) { |
| this.provisionList = provisionList; |
| this.expected = ImmutableList.copyOf(expected); |
| } |
| |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| List<Class<?>> actual = Lists.newArrayList(); |
| for (DependencyAndSource dep : provision.getDependencyChain()) { |
| actual.add(dep.getDependency().getKey().getRawType()); |
| } |
| assertEquals(expected, actual); |
| provisionList.add(provision.getBinding().getKey().getRawType()); |
| } |
| } |
| |
| private static Matcher<Binding<?>> keyMatcher(final Class<?> clazz) { |
| return new AbstractMatcher<Binding<?>>() { |
| @Override |
| public boolean matches(Binding<?> t) { |
| return t.getKey().equals(Key.get(clazz)); |
| } |
| }; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public void testDependencyChain() { |
| final List<Class<?>> pList = Lists.newArrayList(); |
| final List<Class<?>> totalList = Lists.newArrayList(); |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bind(Instance.class).toInstance(new Instance()); |
| bind(B.class).to(BImpl.class); |
| bind(D.class).toProvider(DP.class); |
| |
| bindListener(Matchers.any(), new ProvisionListener() { |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| totalList.add(provision.getBinding().getKey().getRawType()); |
| } |
| }); |
| |
| // Build up a list of asserters for our dependency chains. |
| ImmutableList.Builder<Class<?>> chain = ImmutableList.builder(); |
| chain.add(Instance.class); |
| bindListener(keyMatcher(Instance.class), new ChainAsserter(pList, chain.build())); |
| |
| chain.add(A.class); |
| bindListener(keyMatcher(A.class), new ChainAsserter(pList, chain.build())); |
| |
| chain.add(B.class).add(BImpl.class); |
| bindListener(keyMatcher(BImpl.class), new ChainAsserter(pList, chain.build())); |
| |
| chain.add(C.class); |
| bindListener(keyMatcher(C.class), new ChainAsserter(pList, chain.build())); |
| |
| // the chain has D before DP even though DP is provisioned & notified first |
| // because we do DP because of D, and need DP to provision D. |
| chain.add(D.class).add(DP.class); |
| bindListener(keyMatcher(D.class), new ChainAsserter(pList, chain.build())); |
| bindListener(keyMatcher(DP.class), new ChainAsserter(pList, chain.build())); |
| |
| chain.add(E.class); |
| bindListener(keyMatcher(E.class), new ChainAsserter(pList, chain.build())); |
| |
| chain.add(F.class); |
| bindListener(keyMatcher(F.class), new ChainAsserter(pList, chain.build())); |
| } |
| @Provides C c(D d) { |
| return new C() {}; |
| } |
| }); |
| Instance instance = injector.getInstance(Instance.class); |
| // make sure we're checking all of the chain asserters.. |
| assertEquals( |
| of(Instance.class, A.class, BImpl.class, C.class, DP.class, D.class, E.class, F.class), |
| pList); |
| // and make sure that nothing else was notified that we didn't expect. |
| assertEquals(totalList, pList); |
| } |
| |
| public void testModuleRequestInjection() { |
| final AtomicBoolean notified = new AtomicBoolean(); |
| Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| requestInjection(new Object() { |
| @Inject Foo foo; |
| }); |
| bindListener(Matchers.any(), |
| new SpecialChecker(Foo.class, getClass().getName() + ".configure(", notified)); |
| } |
| }); |
| assertTrue(notified.get()); |
| } |
| |
| public void testToProviderInstance() { |
| final AtomicBoolean notified = new AtomicBoolean(); |
| Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bind(Object.class).toProvider(new Provider<Object>() { |
| @Inject Foo foo; |
| public Object get() { |
| return null; |
| } |
| }); |
| bindListener(Matchers.any(), |
| new SpecialChecker(Foo.class, getClass().getName() + ".configure(", notified)); |
| } |
| }); |
| assertTrue(notified.get()); |
| } |
| |
| public void testInjectorInjectMembers() { |
| final Object object = new Object() { |
| @Inject Foo foo; |
| }; |
| final AtomicBoolean notified = new AtomicBoolean(); |
| Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), |
| new SpecialChecker(Foo.class, object.getClass().getName(), notified)); |
| } |
| }).injectMembers(object); |
| assertTrue(notified.get()); |
| } |
| |
| private static class SpecialChecker implements ProvisionListener { |
| private final Class<?> notifyType; |
| private final String firstSource; |
| private final AtomicBoolean notified; |
| |
| public SpecialChecker(Class<?> notifyType, String firstSource, AtomicBoolean notified) { |
| this.notifyType = notifyType; |
| this.firstSource = firstSource; |
| this.notified = notified; |
| } |
| |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| notified.set(true); |
| assertEquals(notifyType, provision.getBinding().getKey().getRawType()); |
| assertEquals(2, provision.getDependencyChain().size()); |
| |
| assertEquals(null, provision.getDependencyChain().get(0).getDependency()); |
| assertContains(provision.getDependencyChain().get(0).getBindingSource(), firstSource); |
| |
| assertEquals(notifyType, |
| provision.getDependencyChain().get(1).getDependency().getKey().getRawType()); |
| assertContains(provision.getDependencyChain().get(1).getBindingSource(), |
| notifyType.getName() + ".class("); |
| } |
| } |
| |
| private static class Instance { |
| @Inject A a; |
| } |
| private static class A { |
| @Inject A(B b) {} |
| } |
| private interface B {} |
| private static class BImpl implements B { |
| @Inject void inject(C c) {} |
| } |
| private interface C {} |
| private interface D {} |
| private static class DP implements Provider<D> { |
| @Inject Provider<E> ep; |
| public D get() { |
| ep.get(); |
| return new D() {}; |
| } |
| } |
| private static class E { |
| @SuppressWarnings("unused") |
| @Inject F f; |
| } |
| private static class F { |
| } |
| |
| public void testBindToInjectorWithListeningGivesSaneException() { |
| try { |
| Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), new Counter()); |
| bind(Injector.class).toProvider(Providers.<Injector>of(null)); |
| } |
| }); |
| fail(); |
| } catch (CreationException ce) { |
| assertContains( |
| ce.getMessage(), "Binding to core guice framework type is not allowed: Injector."); |
| } |
| } |
| |
| public void testProvisionIsNotifiedAfterContextsClear() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bindListener(Matchers.any(), new ProvisionListener() { |
| @Override |
| public <T> void onProvision(ProvisionInvocation<T> provision) { |
| Object provisioned = provision.provision(); |
| if (provisioned instanceof X) { |
| ((X)provisioned).init(); |
| } else if (provisioned instanceof Y) { |
| X.createY = false; |
| ((Y)provisioned).init(); |
| } |
| } |
| }); |
| } |
| }); |
| |
| X.createY = true; |
| X x = injector.getInstance(X.class); |
| assertNotSame(x, x.y.x); |
| assertFalse("x.ID: " + x.ID + ", x.y.x.iD: " + x.y.x.ID, x.ID == x.y.x.ID); |
| } |
| |
| private static class X { |
| final static AtomicInteger COUNTER = new AtomicInteger(); |
| static boolean createY; |
| |
| final int ID = COUNTER.getAndIncrement(); |
| final Provider<Y> yProvider; |
| Y y; |
| |
| @Inject X(Provider<Y> yProvider) { |
| this.yProvider = yProvider; |
| } |
| |
| void init() { |
| if (createY) { |
| this.y = yProvider.get(); |
| } |
| } |
| } |
| |
| private static class Y { |
| final Provider<X> xProvider; |
| X x; |
| |
| @Inject Y(Provider<X> xProvider) { |
| this.xProvider = xProvider; |
| } |
| |
| void init() { |
| this.x = xProvider.get(); |
| } |
| } |
| } |