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:
Dave Syer
2019-01-10 09:21:51 +00:00
parent af2497c6d8
commit fd835487d3
3 changed files with 152 additions and 44 deletions

View File

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

View File

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

View File

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