Add server interceptor blending

This commit implements the `@GprcService(blendWithGlobalInterceptors)`
feature. This allows global and per-service server interceptors to be
combined into a single list and then finally sorted (i.e. blended).

Signed-off-by: Chris Bono <chris.bono@gmail.com>
This commit is contained in:
Chris Bono
2024-11-17 22:31:25 -06:00
committed by Dave Syer
parent e3d58cf988
commit 8eb623e82d
4 changed files with 46 additions and 3 deletions

View File

@@ -20,6 +20,9 @@ import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
import org.springframework.util.Assert;
@@ -93,4 +96,33 @@ final class ApplicationContextBeanLookupUtils {
return applicationContext.getBeanProvider(beanType).orderedStream().filter(beanToNameMap::containsKey).toList();
}
/**
* Sorts the supplied list in place using an {@link OrderComparator} that takes the
* {@link Order @Order} annotation on bean factory methods in configuration classes
* into account.
* @param applicationContext the application context
* @param beanType the type of beans in the list
* @param beans the list of beans to sort
*/
static void sortBeansIncludingOrderAnnotation(ApplicationContext applicationContext, Class<?> beanType,
List<?> beans) {
var beanToNameMap = new LinkedHashMap<Object, String>();
applicationContext.getBeansOfType(beanType).forEach((name, bean) -> beanToNameMap.put(bean, name));
beans.sort(OrderComparator.INSTANCE.withSourceProvider(bean -> {
Integer priority = AnnotationAwareOrderComparator.INSTANCE.getPriority(bean);
if (priority != null) {
return (Ordered) () -> priority;
}
// Consult the bean factory method for annotations
String beanName = beanToNameMap.get(bean);
if (beanName != null) {
Order order = applicationContext.findAnnotationOnBean(beanName, Order.class);
if (order != null) {
return (Ordered) order::value;
}
}
return null;
}));
}
}

View File

@@ -76,7 +76,10 @@ public class DefaultGrpcServiceConfigurer implements GrpcServiceConfigurer, Init
Arrays.stream(serviceInfo.interceptorNames())
.forEachOrdered((interceptorBeanName) -> allInterceptors
.add(this.applicationContext.getBean(interceptorBeanName, ServerInterceptor.class)));
// TODO handle blend
if (serviceInfo.blendWithGlobalInterceptors()) {
ApplicationContextBeanLookupUtils.sortBeansIncludingOrderAnnotation(this.applicationContext,
ServerInterceptor.class, allInterceptors);
}
return ServerInterceptors.interceptForward(serviceDef, allInterceptors);
}

View File

@@ -25,7 +25,6 @@ import java.util.function.Function;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
@@ -256,7 +255,6 @@ class DefaultGrpcServiceConfigurerTests {
}
@SuppressWarnings("unchecked")
@Disabled("Needs 'blend interceptors' to be implemented")
@Test
void whenBlendInterceptorsTrueThenGlobalInterceptorsBlended() {
GrpcServiceInfo serviceInfo = new GrpcServiceInfo(

View File

@@ -128,6 +128,16 @@ BindableService myService() {
}
----
[[server-interceptor-blending]]
[TIP]
====
When a service is configured with both global and per-service interceptors, the global interceptors are first applied in their sorted order followed by the per-service interceptors in their sorted order.
However, by setting the `blendWithGlobalInterceptors` attribute on the `@GrpcService` annotation to `"true"` you can change this behavior so that the interceptors are all combined and then sorted according to their bean natural ordering (i.e. `@Order`).
You can use this option if you want to add a per-service interceptor between global interceptors.
====
== Observability
Spring gRPC provides an autoconfigured interceptor that can be used to provide observability to your gRPC services.