From 335a7120d01818ebae3d3674c9bdc58da7ccc085 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 14 Apr 2014 11:10:42 -0700 Subject: [PATCH] Add limitations --- README.md | 85 +++++++++++++++++++ .../guice/EnableGuiceModules.java | 34 ++++++++ .../guice/ModuleRegistryConfiguration.java | 0 .../guice/ModuleBeanWiringTests.java | 28 +++++- 4 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 README.md create mode 100644 src/main/java/org/springframework/guice/EnableGuiceModules.java rename src/{test => main}/java/org/springframework/guice/ModuleRegistryConfiguration.java (100%) diff --git a/README.md b/README.md new file mode 100644 index 0000000..79edb2e --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +This project provides bridges between Spring and Guice so that you can +use one from the other (and vice versa) + +## Using a Spring ApplicationContext as a Module in Guice + +The main bridge in this case is a Guice `Module` that wraps an +existing Spring `ApplicationContext`. Example: + +```java +AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext(ApplicationConfiguration.class); +Injector injector = Guice.createInjector(new SpringModule(context), new MyModule()); +Service service = injector.getInstance(Service.class); +``` + +Note that the `ApplicationContext` in this example might contain the +`Service` definition or it might be in the Guice `Module` +(`MyModule`), or if `Service` is a concrete class it could be neither, +but Guice creates an instance and wires it for us. + +## Using existing Guice Modules in a Spring ApplicationContext + +The main feature here is a Spring `@Configuration` annotation: +`@EnableGuiceModules`. If you have Guice `Modules` that you want to +re-use (e.g. if they come from a third party) you can declare them in +a Spring `ApplicationContext` as `@Beans`, and expose all their +bindings. Example: + +```java +@EnableGuiceModules +@Configuration +public static class TestConfig extends AbstractModule { + + @Bean + public MyModule myModule() { + return new MyModule(); + } + + @Bean + public Spam spam(Service service) { + return new Spam(service); + } + +} +``` + +The `Service` was defined in the Guice module `MyModule`, and then it +was be bound to the autowired `spam()` method when Spring started. + +## Using Guice as an API for accessing a Spring ApplicationContext + +In this case the main feature is an `Injector` implementation that +wraps a Spring `ApplicationContext`. Example: + +```java +AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext(ApplicationConfiguration.class); +Injector injector = new SpringInjector(context); +Service service = injector.getInstance(Service.class); +``` + +If there is a `@Bean` of type `Service` it will be returned from the +`Injector`. But there may actually not be a `@Bean` definition of type +`Service`, and if it is a concrete type then the `Injector` will +create it and autowire its dependencies for you. A side effect of this +is that a `BeanDefinition` *will* be created. + +## Limitations + +* So far there is no support for the Guice SPI methods in + `SpringInjector` so tooling may not work. It wouldn't be hard to do. + +* `SpringInjector` only knows about raw types, so it ignores + additional meta-information in factory requests (like + annotations). Should be easy enough to fix, but some compromises + might hav eto be made. + +* `SpringInjector` has no support for creating child or parent + `Injectors`. Probably not difficult. + +* `SpringModule` treats all beans as singletons. + +* `SpringModule` binds all interfaces of a bean it can find. This will + cause issues sooner rather than later (e.g. when 2 beans implement + the same interface). diff --git a/src/main/java/org/springframework/guice/EnableGuiceModules.java b/src/main/java/org/springframework/guice/EnableGuiceModules.java new file mode 100644 index 0000000..771c965 --- /dev/null +++ b/src/main/java/org/springframework/guice/EnableGuiceModules.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2014 the original author or authors. + * + * 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 org.springframework.guice; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; + +/** + * @author Dave Syer + * + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(ModuleRegistryConfiguration.class) +public @interface EnableGuiceModules { + +} diff --git a/src/test/java/org/springframework/guice/ModuleRegistryConfiguration.java b/src/main/java/org/springframework/guice/ModuleRegistryConfiguration.java similarity index 100% rename from src/test/java/org/springframework/guice/ModuleRegistryConfiguration.java rename to src/main/java/org/springframework/guice/ModuleRegistryConfiguration.java diff --git a/src/test/java/org/springframework/guice/ModuleBeanWiringTests.java b/src/test/java/org/springframework/guice/ModuleBeanWiringTests.java index 944454e..e2efac7 100644 --- a/src/test/java/org/springframework/guice/ModuleBeanWiringTests.java +++ b/src/test/java/org/springframework/guice/ModuleBeanWiringTests.java @@ -13,7 +13,12 @@ package org.springframework.guice; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import com.google.inject.AbstractModule; import com.google.inject.Injector; @@ -24,19 +29,38 @@ import com.google.inject.Injector; */ public class ModuleBeanWiringTests extends AbstractCompleteWiringTests { + private AnnotationConfigApplicationContext context; + @Override protected Injector createInjector() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.register(TestConfig.class, ModuleRegistryConfiguration.class); + context = new AnnotationConfigApplicationContext(); + context.register(TestConfig.class); context.refresh(); return new SpringInjector(context); } + @Test + public void bindToSpringBeanFromGuiceModule() throws Exception { + assertNotNull(context.getBean(Spam.class)); + } + + @EnableGuiceModules + @Configuration public static class TestConfig extends AbstractModule { @Override protected void configure() { bind(Service.class).to(MyService.class); } + + @Bean + public Spam spam(Service service) { + return new Spam(service); + } + } + + protected static class Spam { + public Spam(Service service) { + } } }