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:
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.
If the ApplicationConfiguration
is annotated @GuiceModule
then it
can filter the types of bean that are registered with the Guice
binder. Example:
@Configuration
@GuiceModule(includeFilters=@Filter(pattern=.*\\.Service))
public class ApplicationConfiguration {
@Bean
public MyService service() {
...
}
}
In this case, only bean types (or interfaces) called "Service" will match the include filter, and only those beans will be bound.
If there are multiple @Beans
of the same type in the
ApplicationContext
then the SpringModule
will register them all,
and there will be a runtime exception if an Injector
needs one. As
with normal Spring dependency resolution, you can add the @Primary
marker to a single bean to differentiate and hint to the Injector
which instance to use.
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:
@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:
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.
In the example above, if ApplicationConfiguration
was annotated
@EnableGuiceModules
then there is an Injector
bean already waiting
to be used, including wiring it into the application itself if you
need it. So this works as well:
@Configuration
@EnableGuiceModules
public class ApplicationConfiguration {
@Autowired
private Injector injector;
@Bean
public Foo foo() {
// Guice creates and does the wiring of Foo instead of Spring
return injector.getInstance(Foo.class);
}
}
In this example if the Injector
has a binding for a Provider
of
Foo.class
then the foo()
method is redundant - it is already
resolvable as a Spring dependency. But if Guice is being used as a
factory for new objects that it doesn't have bindings for, then it
makes sense.
Note: if you also have @GuiceModule
in your context, then using
the injector to create a @Bean
directly is a bad idea (there's a
dependency cycle). You can do it, and break the cycle, if you
exclude the @Bean
type from the Injector
bindings using the
@GuiceModule
exclude filters.
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 parentInjectors
. Probably not difficult. -
SpringModule
treats all beans as singletons. -
SpringModule
binds all interfaces of a bean it can find. This should work out OK, as long as those interfaces are not needed for injection (and if there is no@Primary
bean).