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:
@@ -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;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user