From 6574913f2e13657c95dad018bf4d6a1f855caea2 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Sun, 4 Jun 2017 11:05:26 +0100 Subject: [PATCH] Add some docs and a Closeable to BeanFactoryProvider --- README.md | 36 ++++++++ .../guice/module/BeanFactoryProvider.java | 88 ++++++++++++------- 2 files changed, 92 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 6c1bfa6..53b89db 100644 --- a/README.md +++ b/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` 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: diff --git a/src/main/java/org/springframework/guice/module/BeanFactoryProvider.java b/src/main/java/org/springframework/guice/module/BeanFactoryProvider.java index d865a01..516defc 100644 --- a/src/main/java/org/springframework/guice/module/BeanFactoryProvider.java +++ b/src/main/java/org/springframework/guice/module/BeanFactoryProvider.java @@ -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). + * + *
+ * + * 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 { +public class BeanFactoryProvider implements Provider, Closeable { private Class[] config; private String[] basePackages; private List> initializers = new ArrayList>(); + private PartiallyRefreshableApplicationContext context; /** * Create an application context by scanning these base packages. @@ -80,38 +91,54 @@ public class BeanFactoryProvider implements Provider 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 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 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