From bdd9a557a51e288375fb49dbcefd357154098560 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 26 Apr 2019 16:49:16 +0200 Subject: [PATCH] Streamline ReactiveAdapterRegistry.getAdapter usage Closes gh-22842 --- .../org/springframework/core/Conventions.java | 24 ++++------- .../core/ReactiveAdapterRegistry.java | 43 +++++++++++-------- .../annotation/ReactiveTypeHandler.java | 18 ++++---- 3 files changed, 44 insertions(+), 41 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/Conventions.java b/spring-core/src/main/java/org/springframework/core/Conventions.java index 93a8a7eb443..691f1811a8d 100644 --- a/spring-core/src/main/java/org/springframework/core/Conventions.java +++ b/spring-core/src/main/java/org/springframework/core/Conventions.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -118,13 +118,10 @@ public final class Conventions { } else { valueClass = parameter.getParameterType(); - ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance(); - if (reactiveAdapterRegistry.hasAdapters()) { - ReactiveAdapter adapter = reactiveAdapterRegistry.getAdapter(valueClass); - if (adapter != null && !adapter.getDescriptor().isNoValue()) { - reactiveSuffix = ClassUtils.getShortName(valueClass); - valueClass = parameter.nested().getNestedParameterType(); - } + ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(valueClass); + if (adapter != null && !adapter.getDescriptor().isNoValue()) { + reactiveSuffix = ClassUtils.getShortName(valueClass); + valueClass = parameter.nested().getNestedParameterType(); } } @@ -207,13 +204,10 @@ public final class Conventions { } else { valueClass = resolvedType; - ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance(); - if (reactiveAdapterRegistry.hasAdapters()) { - ReactiveAdapter adapter = reactiveAdapterRegistry.getAdapter(valueClass); - if (adapter != null && !adapter.getDescriptor().isNoValue()) { - reactiveSuffix = ClassUtils.getShortName(valueClass); - valueClass = ResolvableType.forMethodReturnType(method).getGeneric().toClass(); - } + ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(valueClass); + if (adapter != null && !adapter.getDescriptor().isNoValue()) { + reactiveSuffix = ClassUtils.getShortName(valueClass); + valueClass = ResolvableType.forMethodReturnType(method).getGeneric().toClass(); } } diff --git a/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java index 4383bb8ad58..7ce7dc8d01c 100644 --- a/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java +++ b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ import org.springframework.util.ReflectionUtils; * *

By default, depending on classpath availability, adapters are registered * for Reactor, RxJava 1, RxJava 2 types, {@link CompletableFuture}, and Java 9+ - * Flow.Publisher. + * {@code Flow.Publisher}. * * @author Rossen Stoyanchev * @author Sebastien Deleuze @@ -54,7 +54,7 @@ public class ReactiveAdapterRegistry { private final boolean reactorPresent; - private final List adapters = new ArrayList<>(32); + private final List adapters = new ArrayList<>(); /** @@ -62,7 +62,6 @@ public class ReactiveAdapterRegistry { * @see #getSharedInstance() */ public ReactiveAdapterRegistry() { - ClassLoader classLoader = ReactiveAdapterRegistry.class.getClassLoader(); // Reactor @@ -104,8 +103,8 @@ public class ReactiveAdapterRegistry { /** * Register a reactive type along with functions to adapt to and from a - * Reactive Streams {@link Publisher}. The functions can assume their - * input is never be {@code null} nor {@link Optional}. + * Reactive Streams {@link Publisher}. The function arguments assume that + * their input is neither {@code null} nor {@link Optional}. */ public void registerReactiveType(ReactiveTypeDescriptor descriptor, Function> toAdapter, Function, Object> fromAdapter) { @@ -120,6 +119,7 @@ public class ReactiveAdapterRegistry { /** * Get the adapter for the given reactive type. + * @return the corresponding adapter, or {@code null} if none available */ @Nullable public ReactiveAdapter getAdapter(Class reactiveType) { @@ -133,34 +133,41 @@ public class ReactiveAdapterRegistry { * (may be {@code null} if a concrete source object is given) * @param source an instance of the reactive type * (i.e. to adapt from; may be {@code null} if the reactive type is specified) + * @return the corresponding adapter, or {@code null} if none available */ @Nullable public ReactiveAdapter getAdapter(@Nullable Class reactiveType, @Nullable Object source) { + if (this.adapters.isEmpty()) { + return null; + } + Object sourceToUse = (source instanceof Optional ? ((Optional) source).orElse(null) : source); Class clazz = (sourceToUse != null ? sourceToUse.getClass() : reactiveType); if (clazz == null) { return null; } - - return this.adapters.stream() - .filter(adapter -> adapter.getReactiveType() == clazz) - .findFirst() - .orElseGet(() -> - this.adapters.stream() - .filter(adapter -> adapter.getReactiveType().isAssignableFrom(clazz)) - .findFirst() - .orElse(null)); + for (ReactiveAdapter adapter : this.adapters) { + if (adapter.getReactiveType() == clazz) { + return adapter; + } + } + for (ReactiveAdapter adapter : this.adapters) { + if (adapter.getReactiveType().isAssignableFrom(clazz)) { + return adapter; + } + } + return null; } /** - * Return a shared default {@code ReactiveAdapterRegistry} instance, lazily - * building it once needed. + * Return a shared default {@code ReactiveAdapterRegistry} instance, + * lazily building it once needed. *

NOTE: We highly recommend passing a long-lived, pre-configured * {@code ReactiveAdapterRegistry} instance for customization purposes. * This accessor is only meant as a fallback for code paths that want to * fall back on a default instance if one isn't provided. - * @return the shared {@code ReactiveAdapterRegistry} instance (never {@code null}) + * @return the shared {@code ReactiveAdapterRegistry} instance * @since 5.0.2 */ public static ReactiveAdapterRegistry getSharedInstance() { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ReactiveTypeHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ReactiveTypeHandler.java index 938d53dad82..0bc88bfca2c 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ReactiveTypeHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ReactiveTypeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,14 +76,14 @@ class ReactiveTypeHandler { private static Log logger = LogFactory.getLog(ReactiveTypeHandler.class); - private final ReactiveAdapterRegistry reactiveRegistry; + private final ReactiveAdapterRegistry adapterRegistry; private final TaskExecutor taskExecutor; - private Boolean taskExecutorWarning; - private final ContentNegotiationManager contentNegotiationManager; + private boolean taskExecutorWarning; + public ReactiveTypeHandler() { this(ReactiveAdapterRegistry.getSharedInstance(), new SyncTaskExecutor(), new ContentNegotiationManager()); @@ -93,10 +93,12 @@ class ReactiveTypeHandler { Assert.notNull(registry, "ReactiveAdapterRegistry is required"); Assert.notNull(executor, "TaskExecutor is required"); Assert.notNull(manager, "ContentNegotiationManager is required"); - this.reactiveRegistry = registry; + this.adapterRegistry = registry; this.taskExecutor = executor; - this.taskExecutorWarning = executor instanceof SimpleAsyncTaskExecutor || executor instanceof SyncTaskExecutor; this.contentNegotiationManager = manager; + + this.taskExecutorWarning = + (executor instanceof SimpleAsyncTaskExecutor || executor instanceof SyncTaskExecutor); } @@ -104,7 +106,7 @@ class ReactiveTypeHandler { * Whether the type can be adapted to a Reactive Streams {@link Publisher}. */ public boolean isReactiveType(Class type) { - return (this.reactiveRegistry.hasAdapters() && this.reactiveRegistry.getAdapter(type) != null); + return (this.adapterRegistry.getAdapter(type) != null); } @@ -119,7 +121,7 @@ class ReactiveTypeHandler { ModelAndViewContainer mav, NativeWebRequest request) throws Exception { Assert.notNull(returnValue, "Expected return value"); - ReactiveAdapter adapter = this.reactiveRegistry.getAdapter(returnValue.getClass()); + ReactiveAdapter adapter = this.adapterRegistry.getAdapter(returnValue.getClass()); Assert.state(adapter != null, () -> "Unexpected return value: " + returnValue); ResolvableType elementType = ResolvableType.forMethodParameter(returnType).getGeneric();