Skip to content

Commit 9980362

Browse files
committed
Merge pull request #40555 from ttddyy
* pr/40555: Polish "Add ProxyConnectionFactoryCustomizer" Add ProxyConnectionFactoryCustomizer Closes gh-40555
2 parents a2378e1 + 7c3576b commit 9980362

File tree

7 files changed

+206
-26
lines changed

7 files changed

+206
-26
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/r2dbc/R2dbcObservationAutoConfiguration.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,36 +30,45 @@
3030
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
3131
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3232
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
33+
import org.springframework.boot.autoconfigure.r2dbc.ProxyConnectionFactoryCustomizer;
3334
import org.springframework.boot.context.properties.EnableConfigurationProperties;
34-
import org.springframework.boot.r2dbc.ConnectionFactoryDecorator;
3535
import org.springframework.boot.r2dbc.OptionsCapableConnectionFactory;
3636
import org.springframework.context.annotation.Bean;
37+
import org.springframework.core.annotation.Order;
3738

3839
/**
3940
* {@link EnableAutoConfiguration Auto-configuration} for R2DBC observability support.
4041
*
4142
* @author Moritz Halbritter
43+
* @author Tadaya Tsuyukubo
4244
* @since 3.2.0
4345
*/
4446
@AutoConfiguration(after = ObservationAutoConfiguration.class)
4547
@ConditionalOnClass({ ConnectionFactory.class, ProxyConnectionFactory.class })
4648
@EnableConfigurationProperties(R2dbcObservationProperties.class)
4749
public class R2dbcObservationAutoConfiguration {
4850

51+
/**
52+
* {@code @Order} value of the observation customizer.
53+
*/
54+
public static final int R2DBC_PROXY_OBSERVATION_CUSTOMIZER_ORDER = 0;
55+
4956
@Bean
57+
@Order(R2DBC_PROXY_OBSERVATION_CUSTOMIZER_ORDER)
5058
@ConditionalOnBean(ObservationRegistry.class)
51-
ConnectionFactoryDecorator connectionFactoryDecorator(R2dbcObservationProperties properties,
59+
ProxyConnectionFactoryCustomizer observationProxyConnectionFactoryCustomizer(R2dbcObservationProperties properties,
5260
ObservationRegistry observationRegistry,
5361
ObjectProvider<QueryObservationConvention> queryObservationConvention,
5462
ObjectProvider<QueryParametersTagProvider> queryParametersTagProvider) {
55-
return (connectionFactory) -> {
63+
return (builder) -> {
64+
ConnectionFactory connectionFactory = builder.getConnectionFactory();
5665
HostAndPort hostAndPort = extractHostAndPort(connectionFactory);
5766
ObservationProxyExecutionListener listener = new ObservationProxyExecutionListener(observationRegistry,
5867
connectionFactory, hostAndPort.host(), hostAndPort.port());
5968
listener.setIncludeParameterValues(properties.isIncludeParameterValues());
6069
queryObservationConvention.ifAvailable(listener::setQueryObservationConvention);
6170
queryParametersTagProvider.ifAvailable(listener::setQueryParametersTagProvider);
62-
return ProxyConnectionFactory.builder(connectionFactory).listener(listener).build();
71+
builder.listener(listener);
6372
};
6473
}
6574

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/r2dbc/R2dbcObservationAutoConfigurationTests.java

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,10 +30,11 @@
3030

3131
import org.springframework.boot.autoconfigure.AutoConfiguration;
3232
import org.springframework.boot.autoconfigure.AutoConfigurations;
33+
import org.springframework.boot.autoconfigure.r2dbc.ProxyConnectionFactoryCustomizer;
34+
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProxyAutoConfiguration;
3335
import org.springframework.boot.context.annotation.ImportCandidates;
3436
import org.springframework.boot.r2dbc.ConnectionFactoryBuilder;
3537
import org.springframework.boot.r2dbc.ConnectionFactoryDecorator;
36-
import org.springframework.boot.test.context.FilteredClassLoader;
3738
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
3839
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3940

@@ -43,11 +44,13 @@
4344
* Tests for {@link R2dbcObservationAutoConfiguration}.
4445
*
4546
* @author Moritz Halbritter
47+
* @author Tadaya Tsuyukubo
4648
*/
4749
class R2dbcObservationAutoConfigurationTests {
4850

4951
private final ApplicationContextRunner runnerWithoutObservationRegistry = new ApplicationContextRunner()
50-
.withConfiguration(AutoConfigurations.of(R2dbcObservationAutoConfiguration.class));
52+
.withConfiguration(
53+
AutoConfigurations.of(R2dbcProxyAutoConfiguration.class, R2dbcObservationAutoConfiguration.class));
5154

5255
private final ApplicationContextRunner runner = this.runnerWithoutObservationRegistry
5356
.withBean(ObservationRegistry.class, ObservationRegistry::create);
@@ -58,27 +61,10 @@ void shouldBeRegisteredInAutoConfigurationImports() {
5861
.contains(R2dbcObservationAutoConfiguration.class.getName());
5962
}
6063

61-
@Test
62-
void shouldSupplyConnectionFactoryDecorator() {
63-
this.runner.run((context) -> assertThat(context).hasSingleBean(ConnectionFactoryDecorator.class));
64-
}
65-
66-
@Test
67-
void shouldNotSupplyBeansIfR2dbcSpiIsNotOnClasspath() {
68-
this.runner.withClassLoader(new FilteredClassLoader("io.r2dbc.spi"))
69-
.run((context) -> assertThat(context).doesNotHaveBean(ConnectionFactoryDecorator.class));
70-
}
71-
72-
@Test
73-
void shouldNotSupplyBeansIfR2dbcProxyIsNotOnClasspath() {
74-
this.runner.withClassLoader(new FilteredClassLoader("io.r2dbc.proxy"))
75-
.run((context) -> assertThat(context).doesNotHaveBean(ConnectionFactoryDecorator.class));
76-
}
77-
7864
@Test
7965
void shouldNotSupplyBeansIfObservationRegistryIsNotPresent() {
8066
this.runnerWithoutObservationRegistry
81-
.run((context) -> assertThat(context).doesNotHaveBean(ConnectionFactoryDecorator.class));
67+
.run((context) -> assertThat(context).doesNotHaveBean(ProxyConnectionFactoryCustomizer.class));
8268
}
8369

8470
@Test

spring-boot-project/spring-boot-autoconfigure/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ dependencies {
5050
optional("io.projectreactor.netty:reactor-netty-http")
5151
optional("io.r2dbc:r2dbc-spi")
5252
optional("io.r2dbc:r2dbc-pool")
53+
optional("io.r2dbc:r2dbc-proxy")
5354
optional("io.rsocket:rsocket-core")
5455
optional("io.rsocket:rsocket-transport-netty")
5556
optional("io.undertow:undertow-servlet")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.r2dbc;
18+
19+
import io.r2dbc.proxy.ProxyConnectionFactory;
20+
21+
/**
22+
* Callback interface that can be used to customize a
23+
* {@link ProxyConnectionFactory.Builder}.
24+
*
25+
* @author Tadaya Tsuyukubo
26+
* @since 3.4.0
27+
*/
28+
public interface ProxyConnectionFactoryCustomizer {
29+
30+
/**
31+
* Callback to customize a {@link ProxyConnectionFactory.Builder} instance.
32+
* @param builder the builder to customize
33+
*/
34+
void customize(ProxyConnectionFactory.Builder builder);
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.r2dbc;
18+
19+
import io.r2dbc.proxy.ProxyConnectionFactory;
20+
import io.r2dbc.spi.ConnectionFactory;
21+
22+
import org.springframework.beans.factory.ObjectProvider;
23+
import org.springframework.boot.autoconfigure.AutoConfiguration;
24+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
25+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
26+
import org.springframework.boot.r2dbc.ConnectionFactoryDecorator;
27+
import org.springframework.context.annotation.Bean;
28+
29+
/**
30+
* {@link EnableAutoConfiguration Auto-configuration} for {@link ProxyConnectionFactory}.
31+
*
32+
* @author Tadaya Tsuyukubo
33+
* @author Moritz Halbritter
34+
* @since 3.4.0
35+
*/
36+
@AutoConfiguration
37+
@ConditionalOnClass({ ConnectionFactory.class, ProxyConnectionFactory.class })
38+
public class R2dbcProxyAutoConfiguration {
39+
40+
@Bean
41+
ConnectionFactoryDecorator connectionFactoryDecorator(
42+
ObjectProvider<ProxyConnectionFactoryCustomizer> customizers) {
43+
return (connectionFactory) -> {
44+
ProxyConnectionFactory.Builder builder = ProxyConnectionFactory.builder(connectionFactory);
45+
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
46+
return builder.build();
47+
};
48+
}
49+
50+
}

spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ org.springframework.boot.autoconfigure.pulsar.PulsarAutoConfiguration
9898
org.springframework.boot.autoconfigure.pulsar.PulsarReactiveAutoConfiguration
9999
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration
100100
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration
101+
org.springframework.boot.autoconfigure.r2dbc.R2dbcProxyAutoConfiguration
101102
org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration
102103
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration
103104
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.r2dbc;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.UUID;
22+
23+
import io.r2dbc.spi.ConnectionFactory;
24+
import org.junit.jupiter.api.Test;
25+
26+
import org.springframework.boot.autoconfigure.AutoConfigurations;
27+
import org.springframework.boot.r2dbc.ConnectionFactoryBuilder;
28+
import org.springframework.boot.r2dbc.ConnectionFactoryDecorator;
29+
import org.springframework.boot.test.context.FilteredClassLoader;
30+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
31+
import org.springframework.context.annotation.Bean;
32+
import org.springframework.context.annotation.Configuration;
33+
import org.springframework.core.annotation.Order;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
37+
/**
38+
* Tests for {@link R2dbcProxyAutoConfiguration}.
39+
*
40+
* @author Tadaya Tsuyukubo
41+
* @author Moritz Halbritter
42+
*/
43+
class R2dbcProxyAutoConfigurationTests {
44+
45+
private final ApplicationContextRunner runner = new ApplicationContextRunner()
46+
.withConfiguration(AutoConfigurations.of(R2dbcProxyAutoConfiguration.class));
47+
48+
@Test
49+
void shouldSupplyConnectionFactoryDecorator() {
50+
this.runner.run((context) -> assertThat(context).hasSingleBean(ConnectionFactoryDecorator.class));
51+
}
52+
53+
@Test
54+
void shouldNotSupplyBeansIfR2dbcSpiIsNotOnClasspath() {
55+
this.runner.withClassLoader(new FilteredClassLoader("io.r2dbc.spi"))
56+
.run((context) -> assertThat(context).doesNotHaveBean(ConnectionFactoryDecorator.class));
57+
}
58+
59+
@Test
60+
void shouldNotSupplyBeansIfR2dbcProxyIsNotOnClasspath() {
61+
this.runner.withClassLoader(new FilteredClassLoader("io.r2dbc.proxy"))
62+
.run((context) -> assertThat(context).doesNotHaveBean(ConnectionFactoryDecorator.class));
63+
}
64+
65+
@Test
66+
void shouldApplyCustomizers() {
67+
this.runner.withUserConfiguration(ProxyConnectionFactoryCustomizerConfig.class).run((context) -> {
68+
ConnectionFactoryDecorator decorator = context.getBean(ConnectionFactoryDecorator.class);
69+
ConnectionFactory connectionFactory = ConnectionFactoryBuilder
70+
.withUrl("r2dbc:h2:mem:///" + UUID.randomUUID())
71+
.build();
72+
decorator.decorate(connectionFactory);
73+
assertThat(context.getBean(ProxyConnectionFactoryCustomizerConfig.class).called).containsExactly("first",
74+
"second");
75+
});
76+
}
77+
78+
@Configuration(proxyBeanMethods = false)
79+
private static final class ProxyConnectionFactoryCustomizerConfig {
80+
81+
private final List<String> called = new ArrayList<>();
82+
83+
@Bean
84+
@Order(1)
85+
ProxyConnectionFactoryCustomizer first() {
86+
return (builder) -> this.called.add("first");
87+
}
88+
89+
@Bean
90+
@Order(2)
91+
ProxyConnectionFactoryCustomizer second() {
92+
return (builder) -> this.called.add("second");
93+
}
94+
95+
}
96+
97+
}

0 commit comments

Comments
 (0)