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:
@@ -33,7 +33,7 @@ import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
@RequiredArgsConstructor
|
||||
public class ModuleEventListener implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
private final ApplicationModulesRuntime modules;
|
||||
private final ApplicationModulesRuntime runtime;
|
||||
private final Supplier<Tracer> tracer;
|
||||
|
||||
/*
|
||||
@@ -51,11 +51,11 @@ public class ModuleEventListener implements ApplicationListener<ApplicationEvent
|
||||
Object object = foo.getPayload();
|
||||
Class<? extends Object> payloadType = object.getClass();
|
||||
|
||||
if (!modules.isApplicationClass(payloadType)) {
|
||||
if (!runtime.isApplicationClass(payloadType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationModule moduleByType = modules.get()
|
||||
ApplicationModule moduleByType = runtime.get()
|
||||
.getModuleByType(payloadType.getSimpleName())
|
||||
.orElse(null);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.springframework.aop.Advisor;
|
||||
@@ -35,24 +36,20 @@ import org.springframework.modulith.model.ApplicationModules;
|
||||
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
|
||||
/**
|
||||
* A {@link BeanPostProcessor} that decorates beans exposed by application modules with an interceptor that registers
|
||||
* module entry and exit to create tracing spans for those invocations.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ModuleTracingBeanPostProcessor extends ModuleTracingSupport implements BeanPostProcessor {
|
||||
|
||||
public static final String MODULE_BAGGAGE_KEY = "org.springframework.modulith.module";
|
||||
|
||||
private final ApplicationModulesRuntime runtime;
|
||||
private final Tracer tracer;
|
||||
private final Supplier<Tracer> tracer;
|
||||
private final Map<String, Advisor> advisors = new HashMap<>();
|
||||
|
||||
public ModuleTracingBeanPostProcessor(ApplicationModulesRuntime runtime, Tracer tracer) {
|
||||
|
||||
super(runtime);
|
||||
|
||||
this.runtime = runtime;
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
|
||||
@@ -60,7 +57,7 @@ public class ModuleTracingBeanPostProcessor extends ModuleTracingSupport impleme
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
Class<?> type = getBeanUserClass(bean, beanName);
|
||||
Class<?> type = runtime.getUserClass(bean, beanName);
|
||||
|
||||
if (!runtime.isApplicationClass(type) || !type.isInstance(bean)) {
|
||||
return bean;
|
||||
@@ -85,7 +82,7 @@ public class ModuleTracingBeanPostProcessor extends ModuleTracingSupport impleme
|
||||
|
||||
return advisors.computeIfAbsent(module.getName(), __ -> {
|
||||
|
||||
Advice interceptor = ModuleEntryInterceptor.of(module, tracer);
|
||||
Advice interceptor = ModuleEntryInterceptor.of(module, tracer.get());
|
||||
MethodMatcher matcher = new ObservableTypeMethodMatcher(type);
|
||||
Pointcut pointcut = new ComposablePointcut(matcher);
|
||||
|
||||
|
||||
@@ -21,24 +21,14 @@ import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
class ModuleTracingSupport implements BeanClassLoaderAware {
|
||||
|
||||
private final ApplicationModulesRuntime runtime;
|
||||
private ClassLoader classLoader;
|
||||
|
||||
protected ModuleTracingSupport(ApplicationModulesRuntime runtime) {
|
||||
|
||||
Assert.notNull(runtime, "ModulesRuntime must not be null!");
|
||||
|
||||
this.runtime = runtime;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader)
|
||||
@@ -48,10 +38,6 @@ class ModuleTracingSupport implements BeanClassLoaderAware {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
protected final Class<?> getBeanUserClass(Object bean, String beanName) {
|
||||
return runtime.getUserClass(bean, beanName);
|
||||
}
|
||||
|
||||
protected final Object addAdvisor(Object bean, Advisor advisor) {
|
||||
return addAdvisor(bean, advisor, __ -> {});
|
||||
}
|
||||
|
||||
@@ -37,18 +37,11 @@ import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
/**
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class SpringDataRestModuleTracingBeanPostProcessor extends ModuleTracingSupport implements BeanPostProcessor {
|
||||
|
||||
private final Tracer tracer;
|
||||
private final ApplicationModulesRuntime runtime;
|
||||
|
||||
public SpringDataRestModuleTracingBeanPostProcessor(ApplicationModulesRuntime runtime, Tracer tracer) {
|
||||
|
||||
super(runtime);
|
||||
|
||||
this.tracer = tracer;
|
||||
this.runtime = runtime;
|
||||
}
|
||||
private final Supplier<Tracer> tracer;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -73,7 +66,7 @@ public class SpringDataRestModuleTracingBeanPostProcessor extends ModuleTracingS
|
||||
private static class DataRestControllerInterceptor implements MethodInterceptor {
|
||||
|
||||
private final Supplier<ApplicationModules> modules;
|
||||
private final Tracer tracer;
|
||||
private final Supplier<Tracer> tracer;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -90,7 +83,7 @@ public class SpringDataRestModuleTracingBeanPostProcessor extends ModuleTracingS
|
||||
|
||||
ObservedModule observed = new DefaultObservedModule(module);
|
||||
|
||||
return ModuleEntryInterceptor.of(observed, tracer).invoke(invocation);
|
||||
return ModuleEntryInterceptor.of(observed, tracer.get()).invoke(invocation);
|
||||
}
|
||||
|
||||
private ApplicationModule getModuleFrom(Object[] arguments) {
|
||||
|
||||
@@ -42,8 +42,8 @@ class ModuleObservabilityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
static ModuleTracingBeanPostProcessor moduleTracingBeanPostProcessor(ApplicationModulesRuntime runtime,
|
||||
Tracer tracer) {
|
||||
return new ModuleTracingBeanPostProcessor(runtime, tracer);
|
||||
ObjectProvider<Tracer> tracer) {
|
||||
return new ModuleTracingBeanPostProcessor(runtime, () -> tracer.getObject());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.modulith.observability.autoconfigure;
|
||||
|
||||
import io.micrometer.tracing.Tracer;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -33,7 +34,8 @@ class SpringDataRestModuleObservabilityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
static SpringDataRestModuleTracingBeanPostProcessor springDataRestModuleTracingBeanPostProcessor(
|
||||
ApplicationModulesRuntime runtime, Tracer tracer) {
|
||||
return new SpringDataRestModuleTracingBeanPostProcessor(runtime, tracer);
|
||||
ApplicationModulesRuntime runtime, ObjectProvider<Tracer> tracer) {
|
||||
|
||||
return new SpringDataRestModuleTracingBeanPostProcessor(runtime, () -> tracer.getObject());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user