Skip to content

Commit b9be40c

Browse files
committed
Add registerReactiveTypeOverride method to ReactiveAdapterRegistry
Closes gh-31047 (cherry picked from commit 389238f)
1 parent f7d4bd1 commit b9be40c

File tree

2 files changed

+66
-6
lines changed

2 files changed

+66
-6
lines changed

spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,45 @@ public boolean hasAdapters() {
135135
* Register a reactive type along with functions to adapt to and from a
136136
* Reactive Streams {@link Publisher}. The function arguments assume that
137137
* their input is neither {@code null} nor {@link Optional}.
138+
* <p>This variant registers the new adapter after existing adapters.
139+
* It will be matched for the exact reactive type if no earlier adapter was
140+
* registered for the specific type, and it will be matched for assignability
141+
* in a second pass if no earlier adapter had an assignable type before.
142+
* @see #registerReactiveTypeOverride
143+
* @see #getAdapter
138144
*/
139145
public void registerReactiveType(ReactiveTypeDescriptor descriptor,
140146
Function<Object, Publisher<?>> toAdapter, Function<Publisher<?>, Object> fromAdapter) {
141147

142-
if (reactorPresent) {
143-
this.adapters.add(new ReactorAdapter(descriptor, toAdapter, fromAdapter));
144-
}
145-
else {
146-
this.adapters.add(new ReactiveAdapter(descriptor, toAdapter, fromAdapter));
147-
}
148+
this.adapters.add(buildAdapter(descriptor, toAdapter, fromAdapter));
149+
}
150+
151+
/**
152+
* Register a reactive type along with functions to adapt to and from a
153+
* Reactive Streams {@link Publisher}. The function arguments assume that
154+
* their input is neither {@code null} nor {@link Optional}.
155+
* <p>This variant registers the new adapter first, effectively overriding
156+
* any previously registered adapters for the same reactive type. This allows
157+
* for overriding existing adapters, in particular default adapters.
158+
* <p>Note that existing adapters for specific types will still match before
159+
* an assignability match with the new adapter. In order to override all
160+
* existing matches, a new reactive type adapter needs to be registered
161+
* for every specific type, not relying on subtype assignability matches.
162+
* @since 5.3.30
163+
* @see #registerReactiveType
164+
* @see #getAdapter
165+
*/
166+
public void registerReactiveTypeOverride(ReactiveTypeDescriptor descriptor,
167+
Function<Object, Publisher<?>> toAdapter, Function<Publisher<?>, Object> fromAdapter) {
168+
169+
this.adapters.add(0, buildAdapter(descriptor, toAdapter, fromAdapter));
170+
}
171+
172+
private ReactiveAdapter buildAdapter(ReactiveTypeDescriptor descriptor,
173+
Function<Object, Publisher<?>> toAdapter, Function<Publisher<?>, Object> fromAdapter) {
174+
175+
return (reactorPresent ? new ReactorAdapter(descriptor, toAdapter, fromAdapter) :
176+
new ReactiveAdapter(descriptor, toAdapter, fromAdapter));
148177
}
149178

150179
/**

spring-core/src/test/java/org/springframework/core/ReactiveAdapterRegistryTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
* Unit tests for {@link ReactiveAdapterRegistry}.
3838
*
3939
* @author Rossen Stoyanchev
40+
* @author Juergen Hoeller
4041
*/
4142
@SuppressWarnings("unchecked")
4243
class ReactiveAdapterRegistryTests {
@@ -52,14 +53,40 @@ void getAdapterForReactiveSubType() {
5253
ReactiveAdapter adapter2 = getAdapter(ExtendedFlux.class);
5354
assertThat(adapter2).isSameAs(adapter1);
5455

56+
// Register regular reactive type (after existing adapters)
5557
this.registry.registerReactiveType(
5658
ReactiveTypeDescriptor.multiValue(ExtendedFlux.class, ExtendedFlux::empty),
5759
o -> (ExtendedFlux<?>) o,
5860
ExtendedFlux::from);
5961

62+
// Matches for ExtendedFlux itself
6063
ReactiveAdapter adapter3 = getAdapter(ExtendedFlux.class);
6164
assertThat(adapter3).isNotNull();
6265
assertThat(adapter3).isNotSameAs(adapter1);
66+
67+
// Does not match for ExtendedFlux subclass since the default Flux adapter
68+
// is being assignability-checked first when no specific match was found
69+
ReactiveAdapter adapter4 = getAdapter(ExtendedExtendedFlux.class);
70+
assertThat(adapter4).isSameAs(adapter1);
71+
72+
// Register reactive type override (before existing adapters)
73+
this.registry.registerReactiveTypeOverride(
74+
ReactiveTypeDescriptor.multiValue(Flux.class, ExtendedFlux::empty),
75+
o -> (ExtendedFlux<?>) o,
76+
ExtendedFlux::from);
77+
78+
// Override match for Flux
79+
ReactiveAdapter adapter5 = getAdapter(Flux.class);
80+
assertThat(adapter5).isNotNull();
81+
assertThat(adapter5).isNotSameAs(adapter1);
82+
83+
// Initially registered adapter specifically matches for ExtendedFlux
84+
ReactiveAdapter adapter6 = getAdapter(ExtendedFlux.class);
85+
assertThat(adapter6).isSameAs(adapter3);
86+
87+
// Override match for ExtendedFlux subclass
88+
ReactiveAdapter adapter7 = getAdapter(ExtendedExtendedFlux.class);
89+
assertThat(adapter7).isSameAs(adapter5);
6390
}
6491

6592

@@ -79,6 +106,10 @@ public void subscribe(CoreSubscriber<? super T> actual) {
79106
}
80107

81108

109+
private static class ExtendedExtendedFlux<T> extends ExtendedFlux<T> {
110+
}
111+
112+
82113
@Nested
83114
class Reactor {
84115

0 commit comments

Comments
 (0)