Ensure *Aware contracts can be satisfied in ModuleRegistryConfiguration
If the BeanFactoryProvider is used there is a partially refreshed ApplicationContext floating around. It works better if it is able to inject Environment etc. via the Aware interfaces into early beans like post processors.
This commit is contained in:
@@ -65,7 +65,8 @@ import org.springframework.guice.module.SpringModule;
|
||||
*/
|
||||
@Configuration
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
|
||||
class ModuleRegistryConfiguration
|
||||
implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
|
||||
|
||||
private static final String SPRING_GUICE_DEDUPE_BINDINGS_PROPERTY_NAME = "spring.guice.dedup";
|
||||
private ApplicationContext applicationContext;
|
||||
@@ -140,7 +141,8 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor
|
||||
String valueAttribute = getValueAttributeForNamed(key.getAnnotation());
|
||||
if (valueAttribute != null) {
|
||||
return valueAttribute + "_" + className;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
@@ -148,9 +150,11 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor
|
||||
private String getValueAttributeForNamed(Annotation annotation) {
|
||||
if (annotation instanceof Named) {
|
||||
return ((Named) annotation).value();
|
||||
} else if (annotation instanceof javax.inject.Named) {
|
||||
}
|
||||
else if (annotation instanceof javax.inject.Named) {
|
||||
return ((javax.inject.Named) annotation).value();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -160,7 +164,7 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor
|
||||
throws BeansException {
|
||||
modules = new ArrayList<Module>(((ConfigurableListableBeanFactory) registry)
|
||||
.getBeansOfType(Module.class).values());
|
||||
modules.add(new SpringModule(this.applicationContext));
|
||||
modules.add(new SpringModule((ConfigurableListableBeanFactory) registry));
|
||||
Map<Key<?>, Binding<?>> bindings = new HashMap<Key<?>, Binding<?>>();
|
||||
List<Element> elements = Elements.getElements(Stage.TOOL, modules);
|
||||
if (applicationContext.getEnvironment().getProperty(
|
||||
@@ -253,20 +257,23 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
beanFactory.registerSingleton("guiceInjectorInitializer", new GuiceInjectorInitializingBeanPostProcessor(){
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if(injectorCreated.compareAndSet(false,true)) {
|
||||
createInjector(modules, beanFactory);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
});
|
||||
beanFactory.registerSingleton("guiceInjectorInitializer",
|
||||
new GuiceInjectorInitializingBeanPostProcessor() {
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean,
|
||||
String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean,
|
||||
String beanName) throws BeansException {
|
||||
if (injectorCreated.compareAndSet(false, true)) {
|
||||
createInjector(modules, beanFactory);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -274,14 +281,12 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
private static class GuiceInjectorInitializingBeanPostProcessor implements BeanPostProcessor, Ordered {
|
||||
|
||||
private static class GuiceInjectorInitializingBeanPostProcessor
|
||||
implements BeanPostProcessor, Ordered {
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ 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,27 +36,26 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.OrderComparator;
|
||||
|
||||
import com.google.inject.spi.ProvisionListener;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 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).
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BeanFactoryProvider implements Provider<ConfigurableListableBeanFactory>, Closeable {
|
||||
public class BeanFactoryProvider
|
||||
implements Provider<ConfigurableListableBeanFactory>, Closeable {
|
||||
|
||||
private Class<?>[] config;
|
||||
private String[] basePackages;
|
||||
@@ -130,17 +131,21 @@ public class BeanFactoryProvider implements Provider<ConfigurableListableBeanFac
|
||||
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));
|
||||
invokeBeanFactoryPostProcessors(getBeanFactory());
|
||||
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||
beanFactory.registerSingleton("refreshListener",
|
||||
new ContextRefreshingProvisionListener(this));
|
||||
prepareBeanFactory(beanFactory);
|
||||
invokeBeanFactoryPostProcessors(beanFactory);
|
||||
}
|
||||
|
||||
private void delayedRefresh() throws BeansException, IllegalStateException {
|
||||
@@ -152,18 +157,21 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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.module;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.guice.annotation.EnableGuiceModules;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class SpringModuleWrappedTests {
|
||||
|
||||
@Test
|
||||
public void testDependenciesFromWrappedModule() {
|
||||
Injector injector = Guice.createInjector(new SpringModule(
|
||||
BeanFactoryProvider.from(TestConfig.class, ModuleProviderConfig.class)));
|
||||
assertNotNull(injector.getInstance(Baz.class));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class TestConfig {
|
||||
@Bean
|
||||
public Baz baz(Service service) {
|
||||
return new Baz(service);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface Service {
|
||||
}
|
||||
|
||||
protected static class MyService implements Service {
|
||||
}
|
||||
|
||||
public static class Foo {
|
||||
|
||||
@Inject
|
||||
public Foo(@Named("service") Service service) {
|
||||
service.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Baz {
|
||||
|
||||
@Inject
|
||||
public Baz(Service service) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGuiceModules
|
||||
protected static class ModuleProviderConfig {
|
||||
|
||||
@Bean
|
||||
public ProviderModule module() {
|
||||
return new ProviderModule();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Foo service(Service service) {
|
||||
return new Foo(service);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class ProviderModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(Service.class).toProvider(() -> new MyService());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user