From 23423b3572c3f14ee7d538ac76326aa462111856 Mon Sep 17 00:00:00 2001 From: Rohit Behera <126186063+r0h1tb@users.noreply.github.com> Date: Sun, 2 Aug 2026 01:09:49 +0530 Subject: [PATCH] Skip gRPC port initializer when spring-grpc is absent GrpcPortInfoApplicationContextInitializer is registered unconditionally via META-INF/spring.factories, and its initialize() unconditionally adds a listener declared as ApplicationListener. When spring-boot-grpc-test is on the classpath but spring-grpc is not, Spring resolves that listener's generic interface to decide whether to deliver an event, and Class.getGenericInterfaces() throws: java.lang.TypeNotPresentException: Type org.springframework.grpc.server.lifecycle.GrpcServerStartedEvent not present at java.base/java.lang.Class.getGenericInterfaces(Class.java:1278) at org.springframework.core.ResolvableType.getInterfaces(ResolvableType.java:544) Guard registration on the event type being present, using ClassUtils.isPresent as LogbackLoggingSystem already does for the SLF4J bridge handler. See gh-50825 --- ...PortInfoApplicationContextInitializer.java | 6 +++++ ...nfoApplicationContextInitializerTests.java | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/module/spring-boot-grpc-test/src/main/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializer.java b/module/spring-boot-grpc-test/src/main/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializer.java index 76f1e0a30673..5d8a7c726423 100644 --- a/module/spring-boot-grpc-test/src/main/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializer.java +++ b/module/spring-boot-grpc-test/src/main/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializer.java @@ -31,6 +31,7 @@ import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; +import org.springframework.util.ClassUtils; import org.springframework.grpc.server.GrpcServerFactory; import org.springframework.grpc.server.InProcessGrpcServerFactory; import org.springframework.grpc.server.lifecycle.GrpcServerStartedEvent; @@ -50,8 +51,13 @@ class GrpcPortInfoApplicationContextInitializer implements ApplicationContextInitializer { + private static final String GRPC_SERVER_STARTED_EVENT = "org.springframework.grpc.server.lifecycle.GrpcServerStartedEvent"; + @Override public void initialize(ConfigurableApplicationContext applicationContext) { + if (!ClassUtils.isPresent(GRPC_SERVER_STARTED_EVENT, applicationContext.getClassLoader())) { + return; + } applicationContext.addApplicationListener(new Listener(applicationContext)); } diff --git a/module/spring-boot-grpc-test/src/test/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializerTests.java b/module/spring-boot-grpc-test/src/test/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializerTests.java index 7e37a43647cd..b76df6a9ca01 100644 --- a/module/spring-boot-grpc-test/src/test/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializerTests.java +++ b/module/spring-boot-grpc-test/src/test/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializerTests.java @@ -16,10 +16,13 @@ package org.springframework.boot.grpc.test.autoconfigure; +import java.io.IOException; + import io.grpc.Server; import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -43,6 +46,29 @@ class GrpcPortInfoApplicationContextInitializerTests { private static final String PORT_PROPERTY = "local.grpc.server.port"; + @Test + void whenGrpcServerStartedEventIsNotOnTheClasspathRegistersNoListener() throws IOException { + // gh-50825: the initializer is registered unconditionally via + // spring.factories, so with spring-grpc absent the listener's generic + // interface cannot be resolved and Spring throws TypeNotPresentException. + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + FilteredClassLoader classLoader = new FilteredClassLoader(GrpcServerStartedEvent.class)) { + context.setClassLoader(classLoader); + int before = context.getApplicationListeners().size(); + new GrpcPortInfoApplicationContextInitializer().initialize(context); + assertThat(context.getApplicationListeners()).hasSize(before); + } + } + + @Test + void whenGrpcServerStartedEventIsOnTheClasspathRegistersListener() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { + int before = context.getApplicationListeners().size(); + new GrpcPortInfoApplicationContextInitializer().initialize(context); + assertThat(context.getApplicationListeners()).hasSize(before + 1); + } + } + @Test void whenServerHasAddressInitializerSetsPortProperty() { NettyGrpcServerFactory factory = mock();