Support LoadBalancer child context creation during AOT processing (#1135)

This commit is contained in:
Olga Maciaszek-Sharma
2022-08-11 17:00:33 +02:00
committed by GitHub
parent 6e610bf5c9
commit 94ab8728fb
8 changed files with 402 additions and 46 deletions

View File

@@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.aot.AotDetector;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.DisposableBean;
@@ -35,8 +36,11 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigRegistry;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.core.env.MapPropertySource;
import org.springframework.util.Assert;
/**
* Creates a set of child contexts that allows a set of Specifications to define the beans
@@ -48,8 +52,8 @@ import org.springframework.core.env.MapPropertySource;
* @author Spencer Gibb
* @author Dave Syer
* @author Tommy Karlsson
* @author Olga Maciaszek-Sharma
*/
// TODO: add javadoc
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
implements DisposableBean, ApplicationContextAware {
@@ -57,7 +61,7 @@ public abstract class NamedContextFactory<C extends NamedContextFactory.Specific
private final String propertyName;
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
private final Map<String, GenericApplicationContext> contexts = new ConcurrentHashMap<>();
private Map<String, C> configurations = new ConcurrentHashMap<>();
@@ -92,8 +96,8 @@ public abstract class NamedContextFactory<C extends NamedContextFactory.Specific
@Override
public void destroy() {
Collection<AnnotationConfigApplicationContext> values = this.contexts.values();
for (AnnotationConfigApplicationContext context : values) {
Collection<GenericApplicationContext> values = this.contexts.values();
for (GenericApplicationContext context : values) {
// This can fail, but it never throws an exception (you see stack traces
// logged as WARN).
context.close();
@@ -101,7 +105,7 @@ public abstract class NamedContextFactory<C extends NamedContextFactory.Specific
this.contexts.clear();
}
protected AnnotationConfigApplicationContext getContext(String name) {
protected GenericApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
@@ -112,8 +116,39 @@ public abstract class NamedContextFactory<C extends NamedContextFactory.Specific
return this.contexts.get(name);
}
protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context;
public void addContext(String contextId, GenericApplicationContext context) {
Assert.notNull(contextId, "contextId cannot be null.");
Assert.notNull(context, "context cannot be null.");
contexts.put(contextId, context);
}
public GenericApplicationContext createContext(String name) {
GenericApplicationContext context = buildContext(name);
registerBeans(name, context);
context.refresh();
return context;
}
public void registerBeans(String name, GenericApplicationContext context) {
Assert.isInstanceOf(AnnotationConfigRegistry.class, context);
AnnotationConfigRegistry registry = (AnnotationConfigRegistry) context;
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
registry.register(configuration);
}
}
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
registry.register(configuration);
}
}
}
registry.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
}
public GenericApplicationContext buildContext(String name) {
GenericApplicationContext context;
if (this.parent != null) {
// jdk11 issue
// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
@@ -126,33 +161,21 @@ public abstract class NamedContextFactory<C extends NamedContextFactory.Specific
else {
beanFactory.setBeanClassLoader(parent.getClassLoader());
}
context = new AnnotationConfigApplicationContext(beanFactory);
context.setClassLoader(this.parent.getClassLoader());
context = AotDetector.useGeneratedArtifacts() ? new GenericApplicationContext(beanFactory)
: new AnnotationConfigApplicationContext(beanFactory);
context.setClassLoader(parent.getClassLoader());
}
else {
context = new AnnotationConfigApplicationContext();
context = AotDetector.useGeneratedArtifacts() ? new GenericApplicationContext()
: new AnnotationConfigApplicationContext();
}
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
context.register(configuration);
}
}
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,
Collections.<String, Object>singletonMap(this.propertyName, name)));
context.getEnvironment().getPropertySources().addFirst(
new MapPropertySource(this.propertySourceName, Collections.singletonMap(this.propertyName, name)));
if (this.parent != null) {
// Uses Environment from parent as well as beans
context.setParent(this.parent);
}
context.setDisplayName(generateDisplayName(name));
context.refresh();
return context;
}
@@ -161,7 +184,7 @@ public abstract class NamedContextFactory<C extends NamedContextFactory.Specific
}
public <T> T getInstance(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
GenericApplicationContext context = getContext(name);
try {
return context.getBean(type);
}
@@ -176,7 +199,7 @@ public abstract class NamedContextFactory<C extends NamedContextFactory.Specific
}
public <T> ObjectProvider<T> getProvider(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
GenericApplicationContext context = getContext(name);
return context.getBeanProvider(type);
}
@@ -187,7 +210,7 @@ public abstract class NamedContextFactory<C extends NamedContextFactory.Specific
@SuppressWarnings("unchecked")
public <T> T getInstance(String name, ResolvableType type) {
AnnotationConfigApplicationContext context = getContext(name);
GenericApplicationContext context = getContext(name);
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type);
if (beanNames.length > 0) {
for (String beanName : beanNames) {
@@ -200,11 +223,15 @@ public abstract class NamedContextFactory<C extends NamedContextFactory.Specific
}
public <T> Map<String, T> getInstances(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
GenericApplicationContext context = getContext(name);
return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);
}
public Map<String, C> getConfigurations() {
return configurations;
}
/**
* Specification with name and configuration.
*/

View File

@@ -38,6 +38,7 @@ import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Spencer Gibb
* @author Tommy Karlsson
* @author Olga Maciaszek-Sharma
*/
public class NamedContextFactoryTests {
@@ -86,8 +87,8 @@ public class NamedContextFactoryTests {
then(barBazes.size()).as("barBazes size was wrong").isEqualTo(2);
// get the contexts before destroy() to verify these are the old ones
AnnotationConfigApplicationContext fooContext = factory.getContext("foo");
AnnotationConfigApplicationContext barContext = factory.getContext("bar");
GenericApplicationContext fooContext = factory.getContext("foo");
GenericApplicationContext barContext = factory.getContext("bar");
then(fooContext.getClassLoader()).as("foo context classloader does not match parent")
.isSameAs(parent.getClassLoader());