Skip to content

Commit

Permalink
Ensure *Aware contracts can be satisfied in ModuleRegistryConfiguration
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Dave Syer committed Jan 10, 2019
1 parent af2497c commit fd83548
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@
*/
@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;
Expand Down Expand Up @@ -140,17 +141,20 @@ private String extractName(Key<?> key) {
String valueAttribute = getValueAttributeForNamed(key.getAnnotation());
if (valueAttribute != null) {
return valueAttribute + "_" + className;
} else {
}
else {
return className;
}
}

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;
}
}
Expand All @@ -160,7 +164,7 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
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(
Expand Down Expand Up @@ -253,35 +257,36 @@ public boolean equals(Object obj) {
@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
public void setApplicationContext(ApplicationContext applicationContext)
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;
}
}
}



Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

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;
Expand All @@ -34,27 +36,26 @@
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;
Expand Down Expand Up @@ -130,17 +131,21 @@ public ConfigurableListableBeanFactory get() {
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 {
Expand All @@ -152,18 +157,21 @@ public void refresh() {
}

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

Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}

}

}

0 comments on commit fd83548

Please sign in to comment.