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); } }