GH-107 - Improve runtime support configuration setup.

The configuration classes declaring SpringBootApplicationRuntime and ApplicationModulesRuntime beans now describe them as infrastructure beans so that they do not get reported as eagly initialized as their initialization will be triggered when creating the BeanPostProcessors our observability support is based on.

The configuration of the observability support now consistently refer to ApplicationModulesRuntime directly and to the Tracer instance via a supplier as the latter is only needed during the actual component invocation while the former is already needed to decide whether to decorate the instances in the first place. Simplify the inheritance arrangement underneath ModuleTracingSupport.

The logging adapter for ApplicationModuleInitializers now properly unwraps proxy types so that the log output uses the user classes properly.
This commit is contained in:
Oliver Drotbohm
2023-01-05 20:00:18 +01:00
parent 262efb5828
commit 5f25974f0d
8 changed files with 54 additions and 56 deletions

View File

@@ -17,6 +17,7 @@ package org.springframework.modulith.runtime.autoconfigure;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -27,6 +28,9 @@ import org.springframework.modulith.runtime.ApplicationRuntime;
import org.springframework.util.ClassUtils;
/**
* {@link ApplicationRuntime} implementation based on an {@link ApplicationContext} and a class that's annotated with
* {@link SpringBootApplication}.
*
* @author Oliver Drotbohm
*/
@RequiredArgsConstructor
@@ -35,6 +39,7 @@ class SpringBootApplicationRuntime implements ApplicationRuntime {
private static final Map<String, Boolean> APPLICATION_CLASSES = new ConcurrentHashMap<>();
private final ApplicationContext context;
private Class<?> mainApplicationClass;
/*
* (non-Javadoc)
@@ -52,9 +57,17 @@ class SpringBootApplicationRuntime implements ApplicationRuntime {
@Override
public Class<?> getMainApplicationClass() {
var mainBeanNames = context.getBeanNamesForAnnotation(SpringBootApplication.class);
if (mainApplicationClass == null) {
return context.getType(mainBeanNames[0]);
// Traverse BeanDefinitions manually to avoid factory beans to be inspected
this.mainApplicationClass = Arrays.stream(context.getBeanDefinitionNames())
.filter(it -> context.findAnnotationOnBean(it, SpringBootApplication.class, false) != null)
.map(context::getType)
.findFirst()
.orElseThrow(() -> new IllegalStateException("Couldn't find a class annotated with @SpringBootApplication!"));
}
return mainApplicationClass;
}
/*
@@ -78,15 +91,18 @@ class SpringBootApplicationRuntime implements ApplicationRuntime {
@Override
public boolean isApplicationClass(Class<?> type) {
return APPLICATION_CLASSES.computeIfAbsent(type.getName(),
it -> {
var applicationClass = getMainApplicationClass();
if (it.startsWith("org.springframework")) {
return false;
}
return APPLICATION_CLASSES.computeIfAbsent(type.getName(), it -> computeIsApplicationClass(it, applicationClass));
}
return it.startsWith(getMainApplicationClass().getPackage().getName())
|| AutoConfigurationPackages.get(context).stream().anyMatch(pkg -> it.startsWith(pkg));
});
private boolean computeIsApplicationClass(String fqn, Class<?> applicationClass) {
if (fqn.startsWith("org.springframework")) {
return false;
}
return fqn.startsWith(applicationClass.getPackage().getName())
|| AutoConfigurationPackages.get(context).stream().anyMatch(pkg -> fqn.startsWith(pkg));
}
}

View File

@@ -23,19 +23,21 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Supplier;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Role;
import org.springframework.modulith.ApplicationModuleInitializer;
import org.springframework.modulith.model.ApplicationModule;
import org.springframework.modulith.model.ApplicationModules;
import org.springframework.modulith.model.FormatableType;
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
import org.springframework.modulith.runtime.ApplicationRuntime;
import org.springframework.util.ClassUtils;
/**
* Auto-configuration to register a {@link SpringBootApplicationRuntime} and {@link ApplicationModulesRuntime} as Spring
@@ -48,12 +50,14 @@ import org.springframework.util.ClassUtils;
class SpringModulithRuntimeAutoConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean(ApplicationRuntime.class)
SpringBootApplicationRuntime modulithsApplicationRuntime(ApplicationContext context) {
return new SpringBootApplicationRuntime(context);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean
ApplicationModulesRuntime modulesRuntime(ApplicationRuntime runtime) {
@@ -93,7 +97,7 @@ class SpringModulithRuntimeAutoConfiguration {
@Override
public void initialize() {
var listenerType = ClassUtils.getUserClass(delegate);
var listenerType = AopUtils.getTargetClass(delegate);
var formattable = FormatableType.of(listenerType);
var formattedListenerType = modules.getModuleByType(listenerType)
@@ -104,7 +108,7 @@ class SpringModulithRuntimeAutoConfiguration {
delegate.initialize();
LOG.debug("{} done.", formattedListenerType);
LOG.debug("Initializing {} done.", formattedListenerType);
}
}