Olga Maciaszek-Sharma
2 years ago
4 changed files with 264 additions and 26 deletions
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* Copyright 2012-2020 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 |
||||
* |
||||
* https://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.cloud.loadbalancer.aot; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; |
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues; |
||||
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
|
||||
/** |
||||
* @author Olga Maciaszek-Sharma |
||||
*/ |
||||
final class ChildContextMappings { |
||||
|
||||
private ChildContextMappings() { |
||||
throw new IllegalStateException("Can't instantiate a utility class"); |
||||
} |
||||
|
||||
static List<Entry> getChildContexts(ConfigurableApplicationContext applicationContext) { |
||||
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); |
||||
String[] beanNames = applicationContext.getBeanNamesForType(LoadBalancerClientSpecification.class); |
||||
List<Entry> contexts = new ArrayList<>(); |
||||
for (String beanName : beanNames) { |
||||
contexts.add(process(beanFactory.getMergedBeanDefinition(beanName))); |
||||
} |
||||
return contexts; |
||||
} |
||||
|
||||
private static Entry process(BeanDefinition beanDefinition) { |
||||
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues(); |
||||
ConstructorArgumentValues.ValueHolder nameValueHolder = constructorArguments.getIndexedArgumentValue(0, null); |
||||
if (nameValueHolder != null) { |
||||
String name = (String) nameValueHolder.getValue(); |
||||
ConstructorArgumentValues.ValueHolder configurationsValueHolder = constructorArguments |
||||
.getIndexedArgumentValue(1, null); |
||||
if (configurationsValueHolder != null) { |
||||
String[] configurations = (String[]) configurationsValueHolder.getValue(); |
||||
return new Entry(name, configurations); |
||||
} |
||||
} |
||||
throw new IllegalArgumentException("Invalid bean definition " + beanDefinition); |
||||
} |
||||
|
||||
record Entry(String name, String[] configurations) { |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
|
||||
/* |
||||
* Copyright 2012-2020 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 |
||||
* |
||||
* https://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.cloud.loadbalancer.aot; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import javax.lang.model.element.Modifier; |
||||
|
||||
import org.springframework.aot.generate.GeneratedMethod; |
||||
import org.springframework.aot.generate.GenerationContext; |
||||
import org.springframework.aot.generate.MethodReference; |
||||
import org.springframework.beans.factory.BeanFactory; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationCode; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; |
||||
import org.springframework.beans.factory.support.RegisteredBean; |
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ApplicationContextInitializer; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.context.aot.ApplicationContextAotGenerator; |
||||
import org.springframework.context.support.GenericApplicationContext; |
||||
import org.springframework.javapoet.ClassName; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* @author Olga Maciaszek-Sharma |
||||
*/ |
||||
public class LoadBalancerChildContextInitializer |
||||
implements BeanRegistrationAotProcessor, BeanRegistrationExcludeFilter { |
||||
|
||||
private final ApplicationContext applicationContext; |
||||
|
||||
private final LoadBalancerClientFactory loadBalancerClientFactory; |
||||
|
||||
private final Map<String, ApplicationContextInitializer<ConfigurableApplicationContext>> applicationContextInitializers; |
||||
|
||||
public LoadBalancerChildContextInitializer(LoadBalancerClientFactory loadBalancerClientFactory, |
||||
ApplicationContext applicationContext) { |
||||
this(loadBalancerClientFactory, applicationContext, new HashMap<>()); |
||||
} |
||||
|
||||
public LoadBalancerChildContextInitializer(LoadBalancerClientFactory loadBalancerClientFactory, |
||||
ApplicationContext applicationContext, |
||||
Map<String, ApplicationContextInitializer<ConfigurableApplicationContext>> applicationContextInitializers) { |
||||
this.loadBalancerClientFactory = loadBalancerClientFactory; |
||||
this.applicationContext = applicationContext; |
||||
this.applicationContextInitializers = applicationContextInitializers; |
||||
} |
||||
|
||||
private void registerBeans(ConfigurableApplicationContext childContext) { |
||||
if (!applicationContextInitializers.isEmpty()) { |
||||
applicationContextInitializers.keySet().stream() |
||||
.filter(contextId -> contextId.equals(childContext.getDisplayName())) |
||||
.forEach(contextId -> applicationContextInitializers.get(contextId).initialize(childContext)); |
||||
return; |
||||
} |
||||
loadBalancerClientFactory.registerBeans(childContext.getDisplayName(), |
||||
(AnnotationConfigApplicationContext) childContext); |
||||
} |
||||
|
||||
@Override |
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { |
||||
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext); |
||||
ConfigurableApplicationContext context = ((ConfigurableApplicationContext) applicationContext); |
||||
BeanFactory applicationBeanFactory = context.getBeanFactory(); |
||||
if (!(registeredBean.getBeanClass().equals(getClass()) |
||||
&& registeredBean.getBeanFactory().equals(applicationBeanFactory))) { |
||||
return null; |
||||
} |
||||
List<ChildContextMappings.Entry> contextsMap = ChildContextMappings.getChildContexts(context); |
||||
Set<ConfigurableApplicationContext> childContextAotContributions = contextsMap.stream() |
||||
.map(this::buildChildContext).collect(Collectors.toSet()); |
||||
return new AotContribution(childContextAotContributions); |
||||
} |
||||
|
||||
private ConfigurableApplicationContext buildChildContext(ChildContextMappings.Entry entry) { |
||||
ConfigurableApplicationContext childContext = loadBalancerClientFactory.buildContext(entry.name()); |
||||
registerBeans(childContext); |
||||
return childContext; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
LoadBalancerChildContextInitializer withApplicationContextInitializers( |
||||
Map<String, ApplicationContextInitializer<? extends ConfigurableApplicationContext>> applicationContextInitializers) { |
||||
Map<String, ApplicationContextInitializer<ConfigurableApplicationContext>> convertedInitializers = new HashMap<>(); |
||||
applicationContextInitializers.keySet() |
||||
.forEach(contextId -> convertedInitializers.put(contextId, |
||||
(ApplicationContextInitializer<ConfigurableApplicationContext>) applicationContextInitializers |
||||
.get(contextId))); |
||||
return new LoadBalancerChildContextInitializer(loadBalancerClientFactory, applicationContext, |
||||
convertedInitializers); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isExcluded(RegisteredBean registeredBean) { |
||||
return false; |
||||
} |
||||
|
||||
private static class AotContribution implements BeanRegistrationAotContribution { |
||||
|
||||
private final Set<GenericApplicationContext> childContexts; |
||||
|
||||
AotContribution(Set<ConfigurableApplicationContext> childContexts) { |
||||
this.childContexts = childContexts.stream().filter(GenericApplicationContext.class::isInstance) |
||||
.map(GenericApplicationContext.class::cast).collect(Collectors.toSet()); |
||||
} |
||||
|
||||
@Override |
||||
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) { |
||||
ApplicationContextAotGenerator contextAotGenerator = new ApplicationContextAotGenerator(); |
||||
Map<String, ClassName> generatedInitializerClassNames = childContexts.stream().map(childContext -> { |
||||
GenerationContext childGenerationContext = generationContext.withName(childContext.getDisplayName()); |
||||
ClassName initializerClassName = contextAotGenerator.generateApplicationContext(childContext, |
||||
childGenerationContext); |
||||
return Map.entry(childContext.getDisplayName(), initializerClassName); |
||||
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
||||
GeneratedMethod postProcessorMethod = beanRegistrationCode.getMethodGenerator() |
||||
.generateMethod("addChildContextInitializer").using(builder -> { |
||||
builder.addJavadoc("Use AOT child context management initialization"); |
||||
builder.addModifiers(Modifier.PRIVATE, Modifier.STATIC); |
||||
builder.addParameter(RegisteredBean.class, "registeredBean"); |
||||
builder.addParameter(LoadBalancerChildContextInitializer.class, "instance"); |
||||
builder.returns(LoadBalancerChildContextInitializer.class); |
||||
builder.addStatement( |
||||
"Map<String, ApplicationContextInitializer<? extends ConfigurableApplicationContext> applicationContextInitializer> initializers = new HashMap();"); |
||||
generatedInitializerClassNames.keySet() |
||||
.forEach(contextId -> builder.addStatement("initializers.put($S, new $L())", contextId, |
||||
generatedInitializerClassNames.get(contextId))); |
||||
builder.addStatement("return instance.withApplicationContextInitializers(initializers)"); |
||||
}); |
||||
beanRegistrationCode.addInstancePostProcessor( |
||||
MethodReference.ofStatic(beanRegistrationCode.getClassName(), postProcessorMethod.getName())); |
||||
System.out.println("test"); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue