From 5f25974f0dfa8126bf8965fbb50e911346f50d62 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Thu, 5 Jan 2023 20:00:18 +0100 Subject: [PATCH] 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. --- .../observability/ModuleEventListener.java | 6 ++-- .../ModuleTracingBeanPostProcessor.java | 19 +++++----- .../observability/ModuleTracingSupport.java | 14 -------- ...ataRestModuleTracingBeanPostProcessor.java | 15 +++----- .../ModuleObservabilityAutoConfiguration.java | 4 +-- ...tModuleObservabilityAutoConfiguration.java | 6 ++-- .../SpringBootApplicationRuntime.java | 36 +++++++++++++------ ...pringModulithRuntimeAutoConfiguration.java | 10 ++++-- 8 files changed, 54 insertions(+), 56 deletions(-) diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEventListener.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEventListener.java index f874821f..79b5b94b 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEventListener.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEventListener.java @@ -33,7 +33,7 @@ import org.springframework.modulith.runtime.ApplicationModulesRuntime; @RequiredArgsConstructor public class ModuleEventListener implements ApplicationListener { - private final ApplicationModulesRuntime modules; + private final ApplicationModulesRuntime runtime; private final Supplier tracer; /* @@ -51,11 +51,11 @@ public class ModuleEventListener implements ApplicationListener 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); diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleTracingBeanPostProcessor.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleTracingBeanPostProcessor.java index 346e7acd..57033cb1 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleTracingBeanPostProcessor.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleTracingBeanPostProcessor.java @@ -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; private final Map 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); diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleTracingSupport.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleTracingSupport.java index 2555f625..ca682882 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleTracingSupport.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleTracingSupport.java @@ -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, __ -> {}); } diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/SpringDataRestModuleTracingBeanPostProcessor.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/SpringDataRestModuleTracingBeanPostProcessor.java index 70d97ff4..6ab9cb66 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/SpringDataRestModuleTracingBeanPostProcessor.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/SpringDataRestModuleTracingBeanPostProcessor.java @@ -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; /* * (non-Javadoc) @@ -73,7 +66,7 @@ public class SpringDataRestModuleTracingBeanPostProcessor extends ModuleTracingS private static class DataRestControllerInterceptor implements MethodInterceptor { private final Supplier modules; - private final Tracer tracer; + private final Supplier 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) { diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/ModuleObservabilityAutoConfiguration.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/ModuleObservabilityAutoConfiguration.java index 73280663..a708a3d8 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/ModuleObservabilityAutoConfiguration.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/ModuleObservabilityAutoConfiguration.java @@ -42,8 +42,8 @@ class ModuleObservabilityAutoConfiguration { @Bean static ModuleTracingBeanPostProcessor moduleTracingBeanPostProcessor(ApplicationModulesRuntime runtime, - Tracer tracer) { - return new ModuleTracingBeanPostProcessor(runtime, tracer); + ObjectProvider tracer) { + return new ModuleTracingBeanPostProcessor(runtime, () -> tracer.getObject()); } @Bean diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/SpringDataRestModuleObservabilityAutoConfiguration.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/SpringDataRestModuleObservabilityAutoConfiguration.java index c08eccf4..f427a5ac 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/SpringDataRestModuleObservabilityAutoConfiguration.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/SpringDataRestModuleObservabilityAutoConfiguration.java @@ -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) { + + return new SpringDataRestModuleTracingBeanPostProcessor(runtime, () -> tracer.getObject()); } } diff --git a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringBootApplicationRuntime.java b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringBootApplicationRuntime.java index c7c63453..14e8b02a 100644 --- a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringBootApplicationRuntime.java +++ b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringBootApplicationRuntime.java @@ -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 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)); } } diff --git a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java index 9b5c283f..83866ee7 100644 --- a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java +++ b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java @@ -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); } }