Add some docs and a Closeable to BeanFactoryProvider

This commit is contained in:
Dave Syer
2017-06-04 11:05:26 +01:00
parent 37d02f639e
commit 6574913f2e
2 changed files with 92 additions and 32 deletions

View File

@@ -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:

View File

@@ -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;
}