Skip to content

Commit a17c308

Browse files
committed
Fix RestClient unnecessary bean creation in Spring Boot autoconfiguration
The RestClientBeanPostProcessor was creating a new RestClient bean every time it processed a bean, even when the OpenTelemetry interceptor was already present. This resulted in unnecessary bean creation and caused issues with proxy/delegate RestClient beans. Changes: - Modified RestClientBeanPostProcessor to track whether the interceptor was actually added during the mutation operation - Return the original bean instance if the interceptor is already present - Only create a new bean instance when the interceptor is actually added - Added test case to verify bean identity is preserved when interceptor exists This fix ensures that: 1. No unnecessary bean creation occurs when the interceptor is already present 2. Proxy/delegate RestClient beans work correctly 3. RestClient beans created with an injected RestClient.Builder that already has the interceptor configured are not unnecessarily mutated Fixes the issue where RestClient beans with the OTel interceptor already configured would still trigger mutation and new bean creation.
1 parent ad6cc1b commit a17c308

File tree

2 files changed

+44
-12
lines changed

2 files changed

+44
-12
lines changed

instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessor.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,25 @@ private static RestClient addRestClientInterceptorIfNotPresent(
4040
RestClient restClient, OpenTelemetry openTelemetry, InstrumentationConfig config) {
4141
ClientHttpRequestInterceptor instrumentationInterceptor = getInterceptor(openTelemetry, config);
4242

43-
return restClient
44-
.mutate()
45-
.requestInterceptors(
46-
interceptors -> {
47-
if (interceptors.stream()
48-
.noneMatch(
49-
interceptor ->
50-
interceptor.getClass() == instrumentationInterceptor.getClass())) {
51-
interceptors.add(0, instrumentationInterceptor);
52-
}
53-
})
54-
.build();
43+
// Use a flag to track if the interceptor was actually added
44+
boolean[] interceptorAdded = {false};
45+
RestClient result =
46+
restClient
47+
.mutate()
48+
.requestInterceptors(
49+
interceptors -> {
50+
if (interceptors.stream()
51+
.noneMatch(
52+
interceptor ->
53+
interceptor.getClass() == instrumentationInterceptor.getClass())) {
54+
interceptors.add(0, instrumentationInterceptor);
55+
interceptorAdded[0] = true;
56+
}
57+
})
58+
.build();
59+
60+
// Return the original bean if no interceptor was added
61+
return interceptorAdded[0] ? result : restClient;
5562
}
5663

5764
static ClientHttpRequestInterceptor getInterceptor(

instrumentation/spring/spring-boot-autoconfigure/src/testSpring3/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientInstrumentationAutoConfigurationTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,29 @@ void defaultConfiguration() {
8787
"otelRestClientBeanPostProcessor", RestClientBeanPostProcessor.class))
8888
.isNotNull());
8989
}
90+
91+
/**
92+
* Tests that the bean post-processor returns the original bean instance when the interceptor is
93+
* already present, avoiding unnecessary bean creation.
94+
*/
95+
@Test
96+
void doesNotCreateNewBeanWhenInterceptorAlreadyPresent() {
97+
contextRunner
98+
.withPropertyValues("otel.instrumentation.spring-web.enabled=true")
99+
.run(
100+
context -> {
101+
RestClient originalBean = context.getBean(RestClient.class);
102+
RestClientBeanPostProcessor postProcessor =
103+
context.getBean(
104+
"otelRestClientBeanPostProcessor", RestClientBeanPostProcessor.class);
105+
106+
// Process the bean again - should return the same instance since interceptor is
107+
// already present
108+
Object processedBean =
109+
postProcessor.postProcessAfterInitialization(originalBean, "restClient");
110+
111+
// Verify that the same instance is returned
112+
assertThat(processedBean).isSameAs(originalBean);
113+
});
114+
}
90115
}

0 commit comments

Comments
 (0)