ReactiveAdapterRegistry detects Reactor

The ReactiveAdapterRegistry now detects the presence of Reactor. In
practice Reactor is required for the Spring Framework reactive support
and it is expected to be present.

The registry however is now capable of being neutral if Reactor is not
present on the classpath for example where other Spring projects may
not have the same assumptions about Reactor's presence.

Issue: SPR-14902
This commit is contained in:
Rossen Stoyanchev 2016-11-27 21:36:14 -05:00
parent 52096ab8b9
commit ac86acde53
2 changed files with 54 additions and 38 deletions

View File

@ -36,12 +36,12 @@ import rx.RxReactiveStreams;
import org.springframework.util.ClassUtils;
/**
* A registry of adapters to adapt to {@link Flux} and {@link Mono}.
* A registry of adapters to adapt a Reactive Streams {@link Publisher} to/from
* various async/reactive types such as {@code CompletableFuture}, RxJava
* {@code Observable}, and others.
*
* <p>By default, depending on classpath availability, adapters are registered
* for RxJava 1, RxJava 2 types, and {@link CompletableFuture}. In addition the
* registry contains adapters for Reactor's own Flux and Mono types (no-op)
* along with adaption for any other Reactive Streams {@link Publisher}.
* for Reactor, RxJava 1, RxJava 2 types, and {@link CompletableFuture}.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
@ -49,6 +49,9 @@ import org.springframework.util.ClassUtils;
*/
public class ReactiveAdapterRegistry {
private static final boolean reactorPresent =
ClassUtils.isPresent("reactor.core.publisher.Flux", ReactiveAdapterRegistry.class.getClassLoader());
private static final boolean rxJava1Present =
ClassUtils.isPresent("rx.Observable", ReactiveAdapterRegistry.class.getClassLoader());
@ -67,35 +70,14 @@ public class ReactiveAdapterRegistry {
*/
public ReactiveAdapterRegistry() {
// Flux and Mono ahead of Publisher...
registerReactiveType(
ReactiveTypeDescriptor.singleOptionalValue(Mono.class, Mono::empty),
source -> (Mono<?>) source,
Mono::from
);
registerReactiveType(ReactiveTypeDescriptor.multiValue(Flux.class, Flux::empty),
source -> (Flux<?>) source,
Flux::from);
registerReactiveType(ReactiveTypeDescriptor.multiValue(Publisher.class, Flux::empty),
source -> (Publisher<?>) source,
source -> source);
registerReactiveType(
ReactiveTypeDescriptor.singleOptionalValue(CompletableFuture.class, () -> {
CompletableFuture<?> empty = new CompletableFuture<>();
empty.complete(null);
return empty;
}),
source -> Mono.fromFuture((CompletableFuture<?>) source),
source -> Mono.from(source).toFuture()
);
if (reactorPresent) {
new ReactorRegistrar().registerAdapters(this);
}
if (rxJava1Present && rxJava1Adapter) {
new RxJava1Registrar().registerAdapters(this);
}
if (rxJava2Present) {
new RxJava2Registrar().registerAdapters(this);
}
@ -110,7 +92,12 @@ public class ReactiveAdapterRegistry {
public void registerReactiveType(ReactiveTypeDescriptor descriptor,
Function<Object, Publisher<?>> toAdapter, Function<Publisher<?>, Object> fromAdapter) {
this.adapters.add(new ReactorAdapter(descriptor, toAdapter, fromAdapter));
if (reactorPresent) {
this.adapters.add(new ReactorAdapter(descriptor, toAdapter, fromAdapter));
}
else {
this.adapters.add(new ReactiveAdapter(descriptor, toAdapter, fromAdapter));
}
}
/**
@ -145,9 +132,41 @@ public class ReactiveAdapterRegistry {
}
private static class ReactorRegistrar {
void registerAdapters(ReactiveAdapterRegistry registry) {
// Flux and Mono ahead of Publisher...
registry.registerReactiveType(
ReactiveTypeDescriptor.singleOptionalValue(Mono.class, Mono::empty),
source -> (Mono<?>) source,
Mono::from
);
registry.registerReactiveType(ReactiveTypeDescriptor.multiValue(Flux.class, Flux::empty),
source -> (Flux<?>) source,
Flux::from);
registry.registerReactiveType(ReactiveTypeDescriptor.multiValue(Publisher.class, Flux::empty),
source -> (Publisher<?>) source,
source -> source);
registry.registerReactiveType(
ReactiveTypeDescriptor.singleOptionalValue(CompletableFuture.class, () -> {
CompletableFuture<?> empty = new CompletableFuture<>();
empty.complete(null);
return empty;
}),
source -> Mono.fromFuture((CompletableFuture<?>) source),
source -> Mono.from(source).toFuture()
);
}
}
private static class RxJava1Registrar {
public void registerAdapters(ReactiveAdapterRegistry registry) {
void registerAdapters(ReactiveAdapterRegistry registry) {
registry.registerReactiveType(
ReactiveTypeDescriptor.multiValue(rx.Observable.class, rx.Observable::empty),
source -> RxReactiveStreams.toPublisher((rx.Observable<?>) source),
@ -168,7 +187,7 @@ public class ReactiveAdapterRegistry {
private static class RxJava2Registrar {
public void registerAdapters(ReactiveAdapterRegistry registry) {
void registerAdapters(ReactiveAdapterRegistry registry) {
registry.registerReactiveType(
ReactiveTypeDescriptor.multiValue(Flowable.class, Flowable::empty),
source -> (Flowable<?>) source,
@ -204,7 +223,7 @@ public class ReactiveAdapterRegistry {
*/
private static class ReactorAdapter extends ReactiveAdapter {
public ReactorAdapter(ReactiveTypeDescriptor descriptor,
ReactorAdapter(ReactiveTypeDescriptor descriptor,
Function<Object, Publisher<?>> toPublisherFunction,
Function<Publisher<?>, Object> fromPublisherFunction) {

View File

@ -17,9 +17,6 @@ package org.springframework.core;
import java.util.function.Supplier;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.util.Assert;
/**
@ -76,9 +73,9 @@ public class ReactiveTypeDescriptor {
/**
* Return {@code true} if the reactive type can produce more than 1 value
* can be produced and is therefore a good fit to adapt to {@link Flux}.
* can be produced and is therefore a good fit to adapt to {@code Flux}.
* A {@code false} return value implies the reactive type can produce 1
* value at most and is therefore a good fit to adapt to {@link Mono}.
* value at most and is therefore a good fit to adapt to {@code Mono}.
*/
public boolean isMultiValue() {
return this.multiValue;