Add some docs and a Closeable to BeanFactoryProvider
This commit is contained in:
36
README.md
36
README.md
@@ -45,6 +45,42 @@ 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.
|
||||
|
||||
## Registering Spring Configuration Classes as a Guice Module
|
||||
|
||||
If your Spring `@Configuration` has dependencies that can only come
|
||||
from a Guice `Module` and you prefer to use the Guice APIs to build up
|
||||
the configuration (so you can't use `@EnableGuiceModules` below), then
|
||||
you can create a `SpringModule` from a
|
||||
`Provider<ConfigurableListableBeanFactory>` instead of from an
|
||||
existing `ApplicationContext`. There are some additional features that
|
||||
may also apply:
|
||||
|
||||
* If the bean factory created by the provider is a
|
||||
`DefaultListableBeanFactory` (mostly it would be if it came from an
|
||||
`ApplicationContext`), then it will pick up a special Guice-aware
|
||||
`AutowireCandidateResolver`, meaning that it will be able to inject
|
||||
dependencies from Guice modules that are not registered as beans.
|
||||
|
||||
* If the bean factory contains any beans of type `ProvisionListener`
|
||||
(a Guice lifecysle listener), then those will be instantiated and
|
||||
registered with Guice.
|
||||
|
||||
To take advantage of the autowiring the bean factory must come from an
|
||||
`ApplicationContext` that is not fully refreshed (refreshing would
|
||||
resolve all the dependencies and fail because the Guice resolver is
|
||||
not yet registered). To help you build bean factories that have this
|
||||
quality there is a convenience class called `BeanFactoryProvider` with
|
||||
static methods which you can use to create a provider to inject into a
|
||||
`SpringModule`. Example:
|
||||
|
||||
```java
|
||||
Injector injector = Guice.createInjector(new SimpleGuiceModule(),
|
||||
new SpringModule(BeanFactoryProvider.from(SpringConfiguration.class)));
|
||||
```
|
||||
|
||||
The `SimpleGuiceModule` contains a component that the
|
||||
`SpringConfiguration` depends on.
|
||||
|
||||
## Using existing Guice Modules in a Spring ApplicationContext
|
||||
|
||||
The main feature here is a Spring `@Configuration` annotation:
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.guice.module;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -23,8 +25,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import com.google.inject.spi.ProvisionListener;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
@@ -34,20 +34,31 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.OrderComparator;
|
||||
|
||||
import com.google.inject.spi.ProvisionListener;
|
||||
|
||||
/**
|
||||
* A {@link Provider} for a {@link BeanFactory} from an {@link ApplicationContext} that
|
||||
* will not be refreshed until the Guice injector wants to resolve dependencies. Delaying
|
||||
* the refresh means that the bean factory can resolve dependencies from Guice modules
|
||||
* (and vice versa).
|
||||
* A {@link Provider} for a {@link BeanFactory} from an
|
||||
* {@link ApplicationContext} that will not be refreshed until the Guice
|
||||
* injector wants to resolve dependencies. Delaying the refresh means that the
|
||||
* bean factory can resolve dependencies from Guice modules (and vice versa).
|
||||
*
|
||||
* <br/>
|
||||
*
|
||||
* Also implements {@link Closeable} so if you want to clean up resources used
|
||||
* in the application context then you can keep a reference to the provider and
|
||||
* call {@link #close()} on it when the application is shut down. Alternatively,
|
||||
* you could register an {@link ApplicationContextInitializer} that sets a
|
||||
* shutdown hook, so that the context is closed automatically when the JVM ends.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BeanFactoryProvider implements Provider<ConfigurableListableBeanFactory> {
|
||||
public class BeanFactoryProvider implements Provider<ConfigurableListableBeanFactory>, Closeable {
|
||||
|
||||
private Class<?>[] config;
|
||||
private String[] basePackages;
|
||||
private List<ApplicationContextInitializer<ConfigurableApplicationContext>> initializers = new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
|
||||
private PartiallyRefreshableApplicationContext context;
|
||||
|
||||
/**
|
||||
* Create an application context by scanning these base packages.
|
||||
@@ -80,38 +91,54 @@ public class BeanFactoryProvider implements Provider<ConfigurableListableBeanFac
|
||||
this.basePackages = basePackages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (this.context != null) {
|
||||
synchronized (this) {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
this.context = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurableListableBeanFactory get() {
|
||||
// TODO: how to close the context?
|
||||
PartiallyRefreshableApplicationContext context = new PartiallyRefreshableApplicationContext();
|
||||
if (config != null && config.length > 0) {
|
||||
context.register(config);
|
||||
}
|
||||
if (basePackages != null && basePackages.length > 0) {
|
||||
context.scan(basePackages);
|
||||
}
|
||||
context.partialRefresh();
|
||||
if (initializers != null && !initializers.isEmpty()) {
|
||||
OrderComparator.sort(initializers);
|
||||
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializers) {
|
||||
initializer.initialize(context);
|
||||
if (this.context == null) {
|
||||
synchronized (this) {
|
||||
if (this.context == null) {
|
||||
PartiallyRefreshableApplicationContext context = new PartiallyRefreshableApplicationContext();
|
||||
if (config != null && config.length > 0) {
|
||||
context.register(config);
|
||||
}
|
||||
if (basePackages != null && basePackages.length > 0) {
|
||||
context.scan(basePackages);
|
||||
}
|
||||
context.partialRefresh();
|
||||
if (initializers != null && !initializers.isEmpty()) {
|
||||
OrderComparator.sort(initializers);
|
||||
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializers) {
|
||||
initializer.initialize(context);
|
||||
}
|
||||
}
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
}
|
||||
return context.getBeanFactory();
|
||||
}
|
||||
|
||||
private static final class PartiallyRefreshableApplicationContext
|
||||
extends AnnotationConfigApplicationContext {
|
||||
private static final class PartiallyRefreshableApplicationContext extends AnnotationConfigApplicationContext {
|
||||
|
||||
private final AtomicBoolean partiallyRefreshed = new AtomicBoolean(false);
|
||||
|
||||
/*
|
||||
* Initializes beanFactoryPostProcessors only to ensure that all BeanDefinition's
|
||||
* are available
|
||||
* Initializes beanFactoryPostProcessors only to ensure that all
|
||||
* BeanDefinition's are available
|
||||
*/
|
||||
private void partialRefresh() {
|
||||
getBeanFactory().registerSingleton("refreshListener",
|
||||
new ContextRefreshingProvisionListener(this));
|
||||
getBeanFactory().registerSingleton("refreshListener", new ContextRefreshingProvisionListener(this));
|
||||
invokeBeanFactoryPostProcessors(getBeanFactory());
|
||||
}
|
||||
|
||||
@@ -124,21 +151,18 @@ public class BeanFactoryProvider implements Provider<ConfigurableListableBeanFac
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeBeanFactoryPostProcessors(
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
|
||||
if (partiallyRefreshed.compareAndSet(false, true)) {
|
||||
super.invokeBeanFactoryPostProcessors(beanFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ContextRefreshingProvisionListener
|
||||
implements ProvisionListener {
|
||||
private static final class ContextRefreshingProvisionListener implements ProvisionListener {
|
||||
private final PartiallyRefreshableApplicationContext context;
|
||||
private final AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
|
||||
private ContextRefreshingProvisionListener(
|
||||
PartiallyRefreshableApplicationContext context) {
|
||||
private ContextRefreshingProvisionListener(PartiallyRefreshableApplicationContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user