GH-746 - Fix observability interception for components declaring event listeners.
This commit is contained in:
@@ -60,6 +60,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.modulith</groupId>
|
||||
<artifactId>spring-modulith-events-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-observation</artifactId>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
@@ -38,10 +39,13 @@ import org.springframework.util.ReflectionUtils;
|
||||
class ObservedModuleType {
|
||||
|
||||
private static Collection<Class<?>> IGNORED_TYPES = List.of(Advised.class, TargetClassAware.class);
|
||||
private static Predicate<Method> IS_USER_METHOD = it -> !Modifier.isPrivate(it.getModifiers())
|
||||
&& !(ReflectionUtils.isObjectMethod(it) || IGNORED_TYPES.contains(it.getDeclaringClass()));
|
||||
|
||||
private final ApplicationModules modules;
|
||||
private final ObservedModule module;
|
||||
private final ArchitecturallyEvidentType type;
|
||||
private final Predicate<Method> methodsToInterceptFilter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ObservedModuleType} for the given {@link ApplicationModules}, {@link ObservedModule} and
|
||||
@@ -60,6 +64,12 @@ class ObservedModuleType {
|
||||
this.modules = modules;
|
||||
this.module = module;
|
||||
this.type = type;
|
||||
|
||||
Predicate<Method> isReferenceMethod = candidate -> type.isEventListener() && type.getReferenceMethods() //
|
||||
.map(ReferenceMethod::getMethod) //
|
||||
.anyMatch(it -> it.reflect().equals(candidate));
|
||||
|
||||
this.methodsToInterceptFilter = IS_USER_METHOD.or(isReferenceMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,20 +90,14 @@ class ObservedModuleType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a predicate to filter the methods to intercept. For event listeners it's the listener methods only. For
|
||||
* everything else, all (public) methods will be intercepted.
|
||||
* Returns a predicate to filter the methods to intercept. All user declared methods are intercepted, except from
|
||||
* well-known interfaces ({@code Advised}, {@code TargetClassAware}). For event listeners, package-protected methods
|
||||
* are supported as well.
|
||||
*
|
||||
* @return
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
public Predicate<Method> getMethodsToIntercept() {
|
||||
|
||||
if (!type.isEventListener()) {
|
||||
return it -> !(ReflectionUtils.isObjectMethod(it) || IGNORED_TYPES.contains(it.getDeclaringClass()));
|
||||
}
|
||||
|
||||
return candidate -> type.getReferenceMethods() //
|
||||
.map(ReferenceMethod::getMethod) //
|
||||
.anyMatch(it -> it.reflect().equals(candidate));
|
||||
return methodsToInterceptFilter;
|
||||
}
|
||||
|
||||
private boolean listensToOtherModulesEvents() {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2023-2024 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 example.sample;
|
||||
|
||||
import org.springframework.aop.TargetClassAware;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.modulith.events.ApplicationModuleListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@Component
|
||||
public abstract class ObservedComponent implements Advised, TargetClassAware {
|
||||
|
||||
@Async
|
||||
public void someMethod() {}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void someInternalMethod() {}
|
||||
|
||||
@ApplicationModuleListener
|
||||
void on(Object event) {}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ package org.springframework.modulith.observability;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.sample.SampleComponent;
|
||||
import example.sample.ObservedComponent;
|
||||
import example.sample.SampleConfiguration;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -38,17 +38,19 @@ class ObservedModuleTypeUnitTests {
|
||||
static final ApplicationModules modules = TestApplicationModules.of("example");
|
||||
|
||||
ApplicationModule module = modules.getModuleByName("sample").orElseThrow();
|
||||
ArchitecturallyEvidentType type = module.getArchitecturallyEvidentType(SampleComponent.class);
|
||||
ArchitecturallyEvidentType type = module.getArchitecturallyEvidentType(ObservedComponent.class);
|
||||
|
||||
ObservedModuleType observedType = new ObservedModuleType(modules, new DefaultObservedModule(module), type);
|
||||
|
||||
@Test // GH-106
|
||||
@Test // GH-106, GH-744
|
||||
void onlyExposesUserMethodsAsToBeIntercepted() {
|
||||
|
||||
assertThat(observedType.getMethodsToIntercept()).satisfies(it -> {
|
||||
|
||||
assertThat(it.test(ReflectionUtils.findMethod(SampleComponent.class, "someMethod"))).isTrue();
|
||||
assertThat(it.test(ReflectionUtils.findMethod(ObservedComponent.class, "someMethod"))).isTrue();
|
||||
assertThat(it.test(ReflectionUtils.findMethod(ObservedComponent.class, "on", Object.class))).isTrue();
|
||||
|
||||
assertThat(it.test(ReflectionUtils.findMethod(ObservedComponent.class, "someInternalMethod"))).isFalse();
|
||||
assertThat(it.test(ReflectionUtils.findMethod(Object.class, "toString"))).isFalse();
|
||||
assertThat(it.test(ReflectionUtils.findMethod(Advised.class, "getTargetClass"))).isFalse();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user