PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/core/test/com/google/inject/BindingTest.java

https://gitlab.com/metamorphiccode/guice
Java | 503 lines | 380 code | 88 blank | 35 comment | 2 complexity | 0df0a44e5ebcd9addbf33afa04f7f30c MD5 | raw file
  1. /*
  2. * Copyright (C) 2007 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. package com.google.inject;
  17. import static com.google.inject.Asserts.assertContains;
  18. import static com.google.inject.name.Names.named;
  19. import com.google.common.collect.ImmutableSet;
  20. import com.google.common.collect.Sets;
  21. import com.google.common.util.concurrent.Runnables;
  22. import com.google.inject.matcher.Matchers;
  23. import com.google.inject.spi.InjectionPoint;
  24. import com.google.inject.spi.TypeEncounter;
  25. import com.google.inject.spi.TypeListener;
  26. import junit.framework.TestCase;
  27. /*if[AOP]*/
  28. import org.aopalliance.intercept.MethodInterceptor;
  29. import org.aopalliance.intercept.MethodInvocation;
  30. /*end[AOP]*/
  31. import java.lang.reflect.Constructor;
  32. import java.util.Collection;
  33. import java.util.List;
  34. import java.util.Map;
  35. import java.util.Set;
  36. import java.util.concurrent.atomic.AtomicInteger;
  37. import java.util.logging.Logger;
  38. /**
  39. * @author crazybob@google.com (Bob Lee)
  40. */
  41. public class BindingTest extends TestCase {
  42. static class Dependent {
  43. @Inject A a;
  44. @Inject Dependent(A a, B b) {}
  45. @Inject void injectBob(Bob bob) {}
  46. }
  47. public void testExplicitCyclicDependency() {
  48. Guice.createInjector(new AbstractModule() {
  49. protected void configure() {
  50. bind(A.class);
  51. bind(B.class);
  52. }
  53. }).getInstance(A.class);
  54. }
  55. static class A { @Inject B b; }
  56. static class B { @Inject A a; }
  57. static class Bob {}
  58. static class MyModule extends AbstractModule {
  59. protected void configure() {
  60. // Linked.
  61. bind(Object.class).to(Runnable.class).in(Scopes.SINGLETON);
  62. // Instance.
  63. bind(Runnable.class).toInstance(Runnables.doNothing());
  64. // Provider instance.
  65. bind(Foo.class).toProvider(new Provider<Foo>() {
  66. public Foo get() {
  67. return new Foo();
  68. }
  69. }).in(Scopes.SINGLETON);
  70. // Provider.
  71. bind(Foo.class)
  72. .annotatedWith(named("provider"))
  73. .toProvider(FooProvider.class);
  74. // Class.
  75. bind(Bar.class).in(Scopes.SINGLETON);
  76. // Constant.
  77. bindConstant().annotatedWith(named("name")).to("Bob");
  78. }
  79. }
  80. static class Foo {}
  81. public static class FooProvider implements Provider<Foo> {
  82. public Foo get() {
  83. throw new UnsupportedOperationException();
  84. }
  85. }
  86. public static class Bar {}
  87. public void testBindToUnboundLinkedBinding() {
  88. try {
  89. Guice.createInjector(new AbstractModule() {
  90. protected void configure() {
  91. bind(Collection.class).to(List.class);
  92. }
  93. });
  94. fail();
  95. } catch (CreationException expected) {
  96. assertContains(expected.getMessage(), "No implementation for java.util.List was bound.");
  97. }
  98. }
  99. /**
  100. * This test ensures that the asEagerSingleton() scoping applies to the key,
  101. * not to what the key is linked to.
  102. */
  103. public void testScopeIsAppliedToKeyNotTarget() {
  104. Injector injector = Guice.createInjector(new AbstractModule() {
  105. protected void configure() {
  106. bind(Integer.class).toProvider(Counter.class).asEagerSingleton();
  107. bind(Number.class).toProvider(Counter.class).asEagerSingleton();
  108. }
  109. });
  110. assertNotSame(injector.getInstance(Integer.class), injector.getInstance(Number.class));
  111. }
  112. static class Counter implements Provider<Integer> {
  113. static AtomicInteger next = new AtomicInteger(1);
  114. public Integer get() {
  115. return next.getAndIncrement();
  116. }
  117. }
  118. public void testAnnotatedNoArgConstructor() {
  119. assertBindingSucceeds(PublicNoArgAnnotated.class);
  120. assertBindingSucceeds(ProtectedNoArgAnnotated.class);
  121. assertBindingSucceeds(PackagePrivateNoArgAnnotated.class);
  122. assertBindingSucceeds(PrivateNoArgAnnotated.class);
  123. }
  124. static class PublicNoArgAnnotated {
  125. @Inject public PublicNoArgAnnotated() { }
  126. }
  127. static class ProtectedNoArgAnnotated {
  128. @Inject protected ProtectedNoArgAnnotated() { }
  129. }
  130. static class PackagePrivateNoArgAnnotated {
  131. @Inject PackagePrivateNoArgAnnotated() { }
  132. }
  133. static class PrivateNoArgAnnotated {
  134. @Inject private PrivateNoArgAnnotated() { }
  135. }
  136. public void testUnannotatedNoArgConstructor() throws Exception{
  137. assertBindingSucceeds(PublicNoArg.class);
  138. assertBindingSucceeds(ProtectedNoArg.class);
  139. assertBindingSucceeds(PackagePrivateNoArg.class);
  140. assertBindingSucceeds(PrivateNoArgInPrivateClass.class);
  141. assertBindingFails(PrivateNoArg.class);
  142. }
  143. static class PublicNoArg {
  144. public PublicNoArg() { }
  145. }
  146. static class ProtectedNoArg {
  147. protected ProtectedNoArg() { }
  148. }
  149. static class PackagePrivateNoArg {
  150. PackagePrivateNoArg() { }
  151. }
  152. private static class PrivateNoArgInPrivateClass {
  153. PrivateNoArgInPrivateClass() { }
  154. }
  155. static class PrivateNoArg {
  156. private PrivateNoArg() { }
  157. }
  158. private void assertBindingSucceeds(final Class<?> clazz) {
  159. assertNotNull(Guice.createInjector().getInstance(clazz));
  160. }
  161. private void assertBindingFails(final Class<?> clazz) throws NoSuchMethodException {
  162. try {
  163. Guice.createInjector().getInstance(clazz);
  164. fail();
  165. } catch (ConfigurationException expected) {
  166. assertContains(expected.getMessage(),
  167. "Could not find a suitable constructor in " + PrivateNoArg.class.getName(),
  168. "at " + PrivateNoArg.class.getName() + ".class(BindingTest.java:");
  169. }
  170. }
  171. public void testTooManyConstructors() {
  172. try {
  173. Guice.createInjector().getInstance(TooManyConstructors.class);
  174. fail();
  175. } catch (ConfigurationException expected) {
  176. assertContains(expected.getMessage(),
  177. TooManyConstructors.class.getName() + " has more than one constructor annotated with "
  178. + "@Inject. Classes must have either one (and only one) constructor",
  179. "at " + TooManyConstructors.class.getName() + ".class(BindingTest.java:");
  180. }
  181. }
  182. static class TooManyConstructors {
  183. @Inject TooManyConstructors(Injector i) {}
  184. @Inject TooManyConstructors() {}
  185. }
  186. public void testToConstructorBinding() throws NoSuchMethodException {
  187. final Constructor<D> constructor = D.class.getConstructor(Stage.class);
  188. Injector injector = Guice.createInjector(new AbstractModule() {
  189. protected void configure() {
  190. bind(Object.class).toConstructor(constructor);
  191. }
  192. });
  193. D d = (D) injector.getInstance(Object.class);
  194. assertEquals(Stage.DEVELOPMENT, d.stage);
  195. }
  196. public void testToConstructorBindingsOnParameterizedTypes() throws NoSuchMethodException {
  197. final Constructor<C> constructor = C.class.getConstructor(Stage.class, Object.class);
  198. final Key<Object> s = new Key<Object>(named("s")) {};
  199. final Key<Object> i = new Key<Object>(named("i")) {};
  200. Injector injector = Guice.createInjector(new AbstractModule() {
  201. protected void configure() {
  202. bind(s).toConstructor(constructor, new TypeLiteral<C<Stage>>() {});
  203. bind(i).toConstructor(constructor, new TypeLiteral<C<Injector>>() {});
  204. }
  205. });
  206. C<Stage> one = (C<Stage>) injector.getInstance(s);
  207. assertEquals(Stage.DEVELOPMENT, one.stage);
  208. assertEquals(Stage.DEVELOPMENT, one.t);
  209. assertEquals(Stage.DEVELOPMENT, one.anotherT);
  210. C<Injector> two = (C<Injector>) injector.getInstance(i);
  211. assertEquals(Stage.DEVELOPMENT, two.stage);
  212. assertEquals(injector, two.t);
  213. assertEquals(injector, two.anotherT);
  214. }
  215. public void testToConstructorBindingsFailsOnRawTypes() throws NoSuchMethodException {
  216. final Constructor constructor = C.class.getConstructor(Stage.class, Object.class);
  217. try {
  218. Guice.createInjector(new AbstractModule() {
  219. protected void configure() {
  220. bind(Object.class).toConstructor(constructor);
  221. }
  222. });
  223. fail();
  224. } catch (CreationException expected) {
  225. assertContains(expected.getMessage(),
  226. "1) T cannot be used as a key; It is not fully specified.",
  227. "at " + C.class.getName() + ".<init>(BindingTest.java:",
  228. "2) T cannot be used as a key; It is not fully specified.",
  229. "at " + C.class.getName() + ".anotherT(BindingTest.java:");
  230. }
  231. }
  232. /*if[AOP]*/
  233. public void testToConstructorAndMethodInterceptors() throws NoSuchMethodException {
  234. final Constructor<D> constructor = D.class.getConstructor(Stage.class);
  235. final AtomicInteger count = new AtomicInteger();
  236. final MethodInterceptor countingInterceptor = new MethodInterceptor() {
  237. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  238. count.incrementAndGet();
  239. return methodInvocation.proceed();
  240. }
  241. };
  242. Injector injector = Guice.createInjector(new AbstractModule() {
  243. protected void configure() {
  244. bind(Object.class).toConstructor(constructor);
  245. bindInterceptor(Matchers.any(), Matchers.any(), countingInterceptor);
  246. }
  247. });
  248. D d = (D) injector.getInstance(Object.class);
  249. d.hashCode();
  250. d.hashCode();
  251. assertEquals(2, count.get());
  252. }
  253. /*end[AOP]*/
  254. public void testInaccessibleConstructor() throws NoSuchMethodException {
  255. final Constructor<E> constructor = E.class.getDeclaredConstructor(Stage.class);
  256. Injector injector = Guice.createInjector(new AbstractModule() {
  257. protected void configure() {
  258. bind(E.class).toConstructor(constructor);
  259. }
  260. });
  261. E e = injector.getInstance(E.class);
  262. assertEquals(Stage.DEVELOPMENT, e.stage);
  263. }
  264. public void testToConstructorAndScopes() throws NoSuchMethodException {
  265. final Constructor<F> constructor = F.class.getConstructor(Stage.class);
  266. final Key<Object> d = Key.get(Object.class, named("D")); // default scoping
  267. final Key<Object> s = Key.get(Object.class, named("S")); // singleton
  268. final Key<Object> n = Key.get(Object.class, named("N")); // "N" instances
  269. final Key<Object> r = Key.get(Object.class, named("R")); // a regular binding
  270. Injector injector = Guice.createInjector(new AbstractModule() {
  271. protected void configure() {
  272. bind(d).toConstructor(constructor);
  273. bind(s).toConstructor(constructor).in(Singleton.class);
  274. bind(n).toConstructor(constructor).in(Scopes.NO_SCOPE);
  275. bind(r).to(F.class);
  276. }
  277. });
  278. assertDistinct(injector, 1, d, d, d, d);
  279. assertDistinct(injector, 1, s, s, s, s);
  280. assertDistinct(injector, 4, n, n, n, n);
  281. assertDistinct(injector, 1, r, r, r, r);
  282. assertDistinct(injector, 4, d, d, r, r, s, s, n);
  283. }
  284. public void assertDistinct(Injector injector, int expectedCount, Key<?>... keys) {
  285. ImmutableSet.Builder<Object> builder = ImmutableSet.builder();
  286. for (Key<?> k : keys) {
  287. builder.add(injector.getInstance(k));
  288. }
  289. assertEquals(expectedCount, builder.build().size());
  290. }
  291. public void testToConstructorSpiData() throws NoSuchMethodException {
  292. final Set<TypeLiteral<?>> heardTypes = Sets.newHashSet();
  293. final Constructor<D> constructor = D.class.getConstructor(Stage.class);
  294. final TypeListener listener = new TypeListener() {
  295. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  296. if (!heardTypes.add(type)) {
  297. fail("Heard " + type + " multiple times!");
  298. }
  299. }
  300. };
  301. Guice.createInjector(new AbstractModule() {
  302. protected void configure() {
  303. bind(Object.class).toConstructor(constructor);
  304. bind(D.class).toConstructor(constructor);
  305. bindListener(Matchers.any(), listener);
  306. }
  307. });
  308. assertEquals(ImmutableSet.of(TypeLiteral.get(D.class)), heardTypes);
  309. }
  310. public void testInterfaceToImplementationConstructor() throws NoSuchMethodException {
  311. final Constructor<CFoo> constructor = CFoo.class.getDeclaredConstructor();
  312. Injector injector = Guice.createInjector(new AbstractModule() {
  313. protected void configure() {
  314. bind(IFoo.class).toConstructor(constructor);
  315. }
  316. });
  317. injector.getInstance(IFoo.class);
  318. }
  319. public static interface IFoo {}
  320. public static class CFoo implements IFoo {}
  321. public void testGetAllBindings() {
  322. Injector injector = Guice.createInjector(new AbstractModule() {
  323. protected void configure() {
  324. bind(D.class).toInstance(new D(Stage.PRODUCTION));
  325. bind(Object.class).to(D.class);
  326. getProvider(new Key<C<Stage>>() {});
  327. }
  328. });
  329. Map<Key<?>,Binding<?>> bindings = injector.getAllBindings();
  330. assertEquals(ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(D.class),
  331. Key.get(Logger.class), Key.get(Object.class), new Key<C<Stage>>() {}),
  332. bindings.keySet());
  333. // add a JIT binding
  334. injector.getInstance(F.class);
  335. Map<Key<?>,Binding<?>> bindings2 = injector.getAllBindings();
  336. assertEquals(ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(D.class),
  337. Key.get(Logger.class), Key.get(Object.class), new Key<C<Stage>>() {}, Key.get(F.class)),
  338. bindings2.keySet());
  339. // the original map shouldn't have changed
  340. assertEquals(ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(D.class),
  341. Key.get(Logger.class), Key.get(Object.class), new Key<C<Stage>>() {}),
  342. bindings.keySet());
  343. // check the bindings' values
  344. assertEquals(injector, bindings.get(Key.get(Injector.class)).getProvider().get());
  345. }
  346. public void testGetAllServletBindings() throws Exception {
  347. Injector injector = Guice.createInjector(new AbstractModule() {
  348. protected void configure() {
  349. bind(F.class); // an explicit binding that uses a JIT binding for a constructor
  350. }
  351. });
  352. injector.getAllBindings();
  353. }
  354. public static class C<T> {
  355. private Stage stage;
  356. private T t;
  357. @Inject T anotherT;
  358. public C(Stage stage, T t) {
  359. this.stage = stage;
  360. this.t = t;
  361. }
  362. @Inject C() {}
  363. }
  364. public static class D {
  365. Stage stage;
  366. public D(Stage stage) {
  367. this.stage = stage;
  368. }
  369. }
  370. private static class E {
  371. Stage stage;
  372. private E(Stage stage) {
  373. this.stage = stage;
  374. }
  375. }
  376. @Singleton
  377. public static class F {
  378. Stage stage;
  379. @Inject public F(Stage stage) {
  380. this.stage = stage;
  381. }
  382. }
  383. public void testTurkeyBaconProblemUsingToConstuctor() {
  384. Injector injector = Guice.createInjector(new AbstractModule() {
  385. @SuppressWarnings("unchecked")
  386. @Override
  387. public void configure() {
  388. bind(Bacon.class).to(UncookedBacon.class);
  389. bind(Bacon.class).annotatedWith(named("Turkey")).to(TurkeyBacon.class);
  390. bind(Bacon.class).annotatedWith(named("Cooked")).toConstructor(
  391. (Constructor)InjectionPoint.forConstructorOf(Bacon.class).getMember());
  392. }
  393. });
  394. Bacon bacon = injector.getInstance(Bacon.class);
  395. assertEquals(Food.PORK, bacon.getMaterial());
  396. assertFalse(bacon.isCooked());
  397. Bacon turkeyBacon = injector.getInstance(Key.get(Bacon.class, named("Turkey")));
  398. assertEquals(Food.TURKEY, turkeyBacon.getMaterial());
  399. assertTrue(turkeyBacon.isCooked());
  400. Bacon cookedBacon = injector.getInstance(Key.get(Bacon.class, named("Cooked")));
  401. assertEquals(Food.PORK, cookedBacon.getMaterial());
  402. assertTrue(cookedBacon.isCooked());
  403. }
  404. enum Food { TURKEY, PORK }
  405. private static class Bacon {
  406. public Food getMaterial() { return Food.PORK; }
  407. public boolean isCooked() { return true; }
  408. }
  409. private static class TurkeyBacon extends Bacon {
  410. public Food getMaterial() { return Food.TURKEY; }
  411. }
  412. private static class UncookedBacon extends Bacon {
  413. public boolean isCooked() { return false; }
  414. }
  415. }