diff --git a/src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java b/src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java index 532211dca..1b3094a5c 100644 --- a/src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java +++ b/src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java @@ -49,6 +49,7 @@ import org.springframework.util.ClassUtils; * wrapper types might be supported/on the class path but conversion may require additional dependencies. * * @author Mark Paluch + * @author Oliver Gierke * @since 2.0 * @see ReactiveWrappers * @see ReactiveAdapterRegistry @@ -167,7 +168,8 @@ public class ReactiveWrapperConverters { * @return {@literal true} if the {@code type} is a supported reactive wrapper type. */ public static boolean supports(Class type) { - return RegistryHolder.REACTIVE_ADAPTER_REGISTRY.getAdapterFrom(type) != null; + return RegistryHolder.REACTIVE_ADAPTER_REGISTRY != null + && RegistryHolder.REACTIVE_ADAPTER_REGISTRY.getAdapterFrom(type) != null; } /** @@ -1007,6 +1009,16 @@ public class ReactiveWrapperConverters { * @author 2.0 */ static class RegistryHolder { - static final ReactiveAdapterRegistry REACTIVE_ADAPTER_REGISTRY = new ReactiveAdapterRegistry(); + + static final ReactiveAdapterRegistry REACTIVE_ADAPTER_REGISTRY; + + static { + + if (ReactiveWrappers.isAvailable(ReactiveLibrary.PROJECT_REACTOR)) { + REACTIVE_ADAPTER_REGISTRY = new ReactiveAdapterRegistry(); + } else { + REACTIVE_ADAPTER_REGISTRY = null; + } + } } } diff --git a/src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java b/src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java index b0b1f5c0d..d070e5db0 100644 --- a/src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java +++ b/src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java @@ -15,6 +15,9 @@ */ package org.springframework.data.repository.util; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.Value; import lombok.experimental.UtilityClass; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -32,7 +35,7 @@ import java.util.Optional; import java.util.stream.Collectors; import org.reactivestreams.Publisher; -import org.springframework.core.ReactiveAdapter.Descriptor; +import org.springframework.core.ReactiveAdapter; import org.springframework.data.util.ReflectionUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -89,25 +92,25 @@ public class ReactiveWrappers { if (RXJAVA1_PRESENT) { - reactiveWrappers.put(Single.class, new Descriptor(false, true, false)); - reactiveWrappers.put(Completable.class, new Descriptor(false, true, true)); - reactiveWrappers.put(Observable.class, new Descriptor(true, true, false)); + reactiveWrappers.put(Single.class, Descriptor.forSingleValue().forValue()); + reactiveWrappers.put(Completable.class, Descriptor.forSingleValue().forNoValue()); + reactiveWrappers.put(Observable.class, Descriptor.forMultiValue().forValue()); } if (RXJAVA2_PRESENT) { - reactiveWrappers.put(io.reactivex.Single.class, new Descriptor(false, true, false)); - reactiveWrappers.put(io.reactivex.Maybe.class, new Descriptor(false, true, false)); - reactiveWrappers.put(io.reactivex.Completable.class, new Descriptor(false, true, true)); - reactiveWrappers.put(io.reactivex.Flowable.class, new Descriptor(true, true, false)); - reactiveWrappers.put(io.reactivex.Observable.class, new Descriptor(true, true, false)); + reactiveWrappers.put(io.reactivex.Single.class, Descriptor.forSingleValue().forValue()); + reactiveWrappers.put(io.reactivex.Maybe.class, Descriptor.forSingleValue().forValue()); + reactiveWrappers.put(io.reactivex.Completable.class, Descriptor.forSingleValue().forNoValue()); + reactiveWrappers.put(io.reactivex.Flowable.class, Descriptor.forMultiValue().forValue()); + reactiveWrappers.put(io.reactivex.Observable.class, Descriptor.forMultiValue().forValue()); } if (PROJECT_REACTOR_PRESENT) { - reactiveWrappers.put(Mono.class, new Descriptor(false, true, false)); - reactiveWrappers.put(Flux.class, new Descriptor(true, true, true)); - reactiveWrappers.put(Publisher.class, new Descriptor(true, true, true)); + reactiveWrappers.put(Mono.class, Descriptor.forSingleValue().forValue()); + reactiveWrappers.put(Flux.class, Descriptor.forMultiValue().forNoValue()); + reactiveWrappers.put(Publisher.class, Descriptor.forMultiValue().forNoValue()); } REACTIVE_WRAPPERS = Collections.unmodifiableMap(reactiveWrappers); @@ -277,4 +280,51 @@ public class ReactiveWrappers { .filter(it -> ClassUtils.isAssignable(it.getKey(), type))// .findFirst().map(it -> it.getValue()); } + + /** + * Basically a copy of Spring's {@link ReactiveAdapter.Descriptor} but without introducing the strong dependency to + * Reactor so that we can safely use the class in non-reactive environments. + * + * @author Oliver Gierke + * @since 2.0 + */ + @Value + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) + private static class Descriptor { + + /** + * Return {@code true} if the adaptee implies 0..N values can be produced and is therefore a good fit to adapt to + * {@link Flux}. A {@code false} return value implies the adaptee will produce 1 value at most and is therefore a + * good fit for {@link Mono}. + */ + private final boolean multiValue; + + /** + * Return {@code true} if the adaptee implies no values will be produced, i.e. providing only completion or error + * signal. + */ + private final boolean noValue; + + public static DescriptorBuilder forSingleValue() { + return new DescriptorBuilder(false); + } + + public static DescriptorBuilder forMultiValue() { + return new DescriptorBuilder(true); + } + + @RequiredArgsConstructor + static class DescriptorBuilder { + + private final boolean multi; + + public Descriptor forValue() { + return new Descriptor(multi, false); + } + + public Descriptor forNoValue() { + return new Descriptor(multi, true); + } + } + } }