diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfiguration.java index 454a7408c9..dd3bd1e874 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfiguration.java @@ -1,7 +1,23 @@ +/* + * Copyright 2012-2021 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.boot.autoconfigure.web.reactive; import java.io.IOException; -import java.io.UncheckedIOException; +import java.nio.file.Path; import java.nio.file.Paths; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -14,67 +30,54 @@ import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; -import org.springframework.http.codec.CodecConfigurer; import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; import org.springframework.util.unit.DataSize; +import org.springframework.web.reactive.config.WebFluxConfigurer; /** * {@link EnableAutoConfiguration Auto-configuration} for multipart support in Spring - * Webflux. - *

- * Configures the {@link DefaultPartHttpMessageReader} via a {@link CodecCustomizer}. + * WebFlux. * * @author Chris Bono + * @author Brian Clozel * @since 2.6.0 */ @Configuration(proxyBeanMethods = false) -@ConditionalOnClass({ CodecConfigurer.class, DefaultPartHttpMessageReader.class }) +@ConditionalOnClass({ DefaultPartHttpMessageReader.class, WebFluxConfigurer.class }) @ConditionalOnWebApplication(type = Type.REACTIVE) @EnableConfigurationProperties(ReactiveMultipartProperties.class) -class ReactiveMultipartAutoConfiguration { +public class ReactiveMultipartAutoConfiguration { @Bean - @Order(1) + @Order(0) CodecCustomizer defaultPartHttpMessageReaderCustomizer(ReactiveMultipartProperties multipartProperties) { return (configurer) -> configurer.defaultCodecs().configureDefaultCodec((codec) -> { - if (!DefaultPartHttpMessageReader.class.isInstance(codec)) { - return; - } - DefaultPartHttpMessageReader defaultPartHttpMessageReader = (DefaultPartHttpMessageReader) codec; - boolean streaming = multipartProperties.getStreaming() != null && multipartProperties.getStreaming(); - boolean unlimitedMaxMemorySize = multipartProperties.getMaxInMemorySize() != null - && multipartProperties.getMaxInMemorySize().toBytes() == -1; - PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); - map.from(multipartProperties::getStreaming).to(defaultPartHttpMessageReader::setStreaming); - if (!streaming) { - map.from(multipartProperties::getMaxInMemorySize).asInt(this::convertToBytes) + if (codec instanceof DefaultPartHttpMessageReader) { + DefaultPartHttpMessageReader defaultPartHttpMessageReader = (DefaultPartHttpMessageReader) codec; + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(multipartProperties::getMaxInMemorySize).asInt(DataSize::toBytes) .to(defaultPartHttpMessageReader::setMaxInMemorySize); - } - if (!streaming && !unlimitedMaxMemorySize) { - map.from(multipartProperties::getMaxDiskUsagePerPart).as(this::convertToBytes) + map.from(multipartProperties::getMaxHeadersSize).asInt(DataSize::toBytes) + .to(defaultPartHttpMessageReader::setMaxHeadersSize); + map.from(multipartProperties::getMaxDiskUsagePerPart).asInt(DataSize::toBytes) .to(defaultPartHttpMessageReader::setMaxDiskUsagePerPart); - map.from(multipartProperties::getFileStorageDirectory) - .to(dir -> setFileStorageDirectory(dir, defaultPartHttpMessageReader)); + map.from(multipartProperties::getMaxParts).to(defaultPartHttpMessageReader::setMaxParts); + map.from(multipartProperties::getStreaming).to(defaultPartHttpMessageReader::setStreaming); + map.from(multipartProperties::getFileStorageDirectory).as(Paths::get) + .to((dir) -> configureFileStorageDirectory(defaultPartHttpMessageReader, dir)); + map.from(multipartProperties::getHeadersCharset).to(defaultPartHttpMessageReader::setHeadersCharset); } - map.from(multipartProperties::getMaxParts).to(defaultPartHttpMessageReader::setMaxParts); - map.from(multipartProperties::getMaxHeadersSize).asInt(this::convertToBytes) - .to(defaultPartHttpMessageReader::setMaxHeadersSize); - map.from(multipartProperties::getHeadersCharset).to(defaultPartHttpMessageReader::setHeadersCharset); }); } - private void setFileStorageDirectory(String fileStorageDirectory, - DefaultPartHttpMessageReader defaultPartHttpMessageReader) { + private void configureFileStorageDirectory(DefaultPartHttpMessageReader defaultPartHttpMessageReader, + Path fileStorageDirectory) { try { - defaultPartHttpMessageReader.setFileStorageDirectory(Paths.get(fileStorageDirectory)); + defaultPartHttpMessageReader.setFileStorageDirectory(fileStorageDirectory); } catch (IOException ex) { - throw new UncheckedIOException(ex); + throw new IllegalStateException("Failed to configure multipart file storage directory", ex); } } - private Long convertToBytes(DataSize size) { - return (size != null) ? size.toBytes() : null; - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartProperties.java index 36392e9b72..bf06bc926c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartProperties.java @@ -34,8 +34,8 @@ import org.springframework.util.unit.DataSize; public class ReactiveMultipartProperties { /** - * Maximum amount of memory allowed per part. Set to -1 to store all contents in - * memory. Ignored when streaming is enabled. + * Maximum amount of memory allowed per part before it's written to disk. Set to -1 to + * store all contents in memory. Ignored when streaming is enabled. */ private DataSize maxInMemorySize = DataSize.ofKilobytes(256); @@ -47,7 +47,7 @@ public class ReactiveMultipartProperties { /** * Maximum amount of disk space allowed per part. Default is -1 which enforces no - * limits. Ignored when streaming is enabled or 'maxInMemorySize' is set to -1. + * limits. Ignored when streaming is enabled. */ private DataSize maxDiskUsagePerPart = DataSize.ofBytes(-1); @@ -58,15 +58,15 @@ public class ReactiveMultipartProperties { private Integer maxParts = -1; /** - * Whether or not to stream directly from the parsed input buffer stream without - * storing in memory nor file. Default is non-streaming. + * Whether to stream directly from the parsed input buffer stream without storing in + * memory nor file. Default is non-streaming. */ private Boolean streaming = Boolean.FALSE; /** - * Directory used to store parts larger than 'maxInMemorySize'. Default is a directory - * named 'spring-multipart' created under the system temporary directory. Ignored when - * streaming is enabled or 'maxInMemorySize' is set to -1. + * Directory used to store file parts larger than 'maxInMemorySize'. Default is a + * directory named 'spring-multipart' created under the system temporary directory. + * Ignored when streaming is enabled. */ private String fileStorageDirectory; @@ -76,7 +76,7 @@ public class ReactiveMultipartProperties { private Charset headersCharset = StandardCharsets.UTF_8; public DataSize getMaxInMemorySize() { - return maxInMemorySize; + return this.maxInMemorySize; } public void setMaxInMemorySize(DataSize maxInMemorySize) { @@ -84,7 +84,7 @@ public class ReactiveMultipartProperties { } public DataSize getMaxHeadersSize() { - return maxHeadersSize; + return this.maxHeadersSize; } public void setMaxHeadersSize(DataSize maxHeadersSize) { @@ -92,7 +92,7 @@ public class ReactiveMultipartProperties { } public DataSize getMaxDiskUsagePerPart() { - return maxDiskUsagePerPart; + return this.maxDiskUsagePerPart; } public void setMaxDiskUsagePerPart(DataSize maxDiskUsagePerPart) { @@ -100,7 +100,7 @@ public class ReactiveMultipartProperties { } public Integer getMaxParts() { - return maxParts; + return this.maxParts; } public void setMaxParts(Integer maxParts) { @@ -108,7 +108,7 @@ public class ReactiveMultipartProperties { } public Boolean getStreaming() { - return streaming; + return this.streaming; } public void setStreaming(Boolean streaming) { @@ -116,7 +116,7 @@ public class ReactiveMultipartProperties { } public String getFileStorageDirectory() { - return fileStorageDirectory; + return this.fileStorageDirectory; } public void setFileStorageDirectory(String fileStorageDirectory) { @@ -124,7 +124,7 @@ public class ReactiveMultipartProperties { } public Charset getHeadersCharset() { - return headersCharset; + return this.headersCharset; } public void setHeadersCharset(Charset headersCharset) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 305690f0ba..ab1d2ac664 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -138,8 +138,8 @@ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\ -org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.ReactiveMultipartAutoConfiguration,\ +org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfigurationTests.java index 5055cfe7d9..27c68bcf80 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfigurationTests.java @@ -16,27 +16,21 @@ package org.springframework.boot.autoconfigure.web.reactive; -import java.lang.reflect.Method; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; -import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.web.codec.CodecCustomizer; -import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.http.codec.CodecConfigurer; import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; import org.springframework.http.codec.support.DefaultServerCodecConfigurer; -import org.springframework.util.ReflectionUtils; import org.springframework.util.unit.DataSize; +import org.springframework.web.reactive.config.WebFluxConfigurer; import static org.assertj.core.api.Assertions.assertThat; @@ -50,18 +44,6 @@ class ReactiveMultipartAutoConfigurationTests { private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ReactiveMultipartAutoConfiguration.class)); - private static final boolean DEFAULT_STREAMING = false; - - private static final int DEFAULT_MAX_IN_MEMORY_SIZE = 256 * 1024; - - private static final int DEFAULT_MAX_PARTS = -1; - - private static final int DEFAULT_MAX_HEADERS_SIZE = 8 * 1024; - - private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; - - private static final long DEFAULT_MAX_DISK_USAGE_PER_PART = -1; - private static final Path DEFAULT_FILE_STORAGE_DIRECTORY = Paths.get(System.getProperty("java.io.tmpdir"), "spring-multipart"); @@ -73,179 +55,37 @@ class ReactiveMultipartAutoConfigurationTests { } @Test - void shouldNotProvideCustomizerWhenDefaultPartHttpMessageReaderNotAvailable() { - this.contextRunner.withClassLoader(new FilteredClassLoader(DefaultPartHttpMessageReader.class)) + void shouldNotProvideCustomizerWhenWebFluxNotAvailable() { + this.contextRunner.withClassLoader(new FilteredClassLoader(WebFluxConfigurer.class)) .run((context) -> assertThat(context).doesNotHaveBean(CodecCustomizer.class)); } @Test - void shouldNotProvideCustomizerWhenCodecConfigurerNotAvailable() { - this.contextRunner.withClassLoader(new FilteredClassLoader(CodecConfigurer.class)) - .run((context) -> assertThat(context).doesNotHaveBean(CodecCustomizer.class)); - } - - @Test - void customizerSetsAppropriatePropsWhenStreamingEnabled() { + void shouldConfigureMultipartProperties() { this.contextRunner.withPropertyValues("spring.webflux.multipart.streaming:true", - "spring.webflux.multipart.max-in-memory-size=1GB", "spring.webflux.multipart.max-headers-size=512MB", + "spring.webflux.multipart.max-in-memory-size=1GB", "spring.webflux.multipart.max-headers-size=16KB", "spring.webflux.multipart.max-disk-usage-per-part=100MB", "spring.webflux.multipart.max-parts=7", - "spring.webflux.multipart.file-storage-directory:.", "spring.webflux.multipart.headers-charset:UTF_16") - .run((context) -> { + "spring.webflux.multipart.headers-charset:UTF_16").run((context) -> { CodecCustomizer customizer = context.getBean(CodecCustomizer.class); DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer(); customizer.customize(configurer); DefaultPartHttpMessageReader partReader = getPartReader(configurer); - - // always set assertThat(partReader).hasFieldOrPropertyWithValue("streaming", true); assertThat(partReader).hasFieldOrPropertyWithValue("maxParts", 7); assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize", - toIntBytes(DataSize.ofMegabytes(512))); + Math.toIntExact(DataSize.ofKilobytes(16).toBytes())); assertThat(partReader).hasFieldOrPropertyWithValue("headersCharset", StandardCharsets.UTF_16); - - // never set when streaming - assertThat(partReader).hasFieldOrPropertyWithValue("maxInMemorySize", DEFAULT_MAX_IN_MEMORY_SIZE); - assertThat(partReader).hasFieldOrPropertyWithValue("maxDiskUsagePerPart", - DEFAULT_MAX_DISK_USAGE_PER_PART); - assertThat(partReader).extracting("fileStorageDirectory") - .asInstanceOf(InstanceOfAssertFactories.type(Mono.class)) - .returns(DEFAULT_FILE_STORAGE_DIRECTORY, Mono::block); - }); - } - - @Test - void customizerSetsAppropriatePropsWhenStreamingDisabledWithUnlimitedMemorySize() { - this.contextRunner.withPropertyValues("spring.webflux.multipart.streaming:false", - "spring.webflux.multipart.max-in-memory-size=-1", "spring.webflux.multipart.max-headers-size=512MB", - "spring.webflux.multipart.max-disk-usage-per-part=100MB", "spring.webflux.multipart.max-parts=7", - "spring.webflux.multipart.file-storage-directory:.", "spring.webflux.multipart.headers-charset:UTF_16") - .run((context) -> { - - CodecCustomizer customizer = context.getBean(CodecCustomizer.class); - DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer(); - customizer.customize(configurer); - DefaultPartHttpMessageReader partReader = getPartReader(configurer); - - // always set - assertThat(partReader).hasFieldOrPropertyWithValue("streaming", false); - assertThat(partReader).hasFieldOrPropertyWithValue("maxParts", 7); - assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize", - toIntBytes(DataSize.ofMegabytes(512))); - assertThat(partReader).hasFieldOrPropertyWithValue("headersCharset", StandardCharsets.UTF_16); - - // set when not streaming w/ unlimited memory size - assertThat(partReader).hasFieldOrPropertyWithValue("maxInMemorySize", -1); - - // never set when not streaming w/ unlimited memory size - assertThat(partReader).hasFieldOrPropertyWithValue("maxDiskUsagePerPart", - DEFAULT_MAX_DISK_USAGE_PER_PART); - assertThat(partReader).extracting("fileStorageDirectory") - .asInstanceOf(InstanceOfAssertFactories.type(Mono.class)) - .returns(DEFAULT_FILE_STORAGE_DIRECTORY, Mono::block); - }); - } - - @Test - void customizerSetsAppropriatePropsWhenStreamingDisabledWithLimitedMemorySize() { - this.contextRunner.withPropertyValues("spring.webflux.multipart.streaming:false", - "spring.webflux.multipart.max-in-memory-size=1GB", "spring.webflux.multipart.max-headers-size=512MB", - "spring.webflux.multipart.max-disk-usage-per-part=100MB", "spring.webflux.multipart.max-parts=7", - "spring.webflux.multipart.file-storage-directory:.", "spring.webflux.multipart.headers-charset:UTF_16") - .run((context) -> { - - CodecCustomizer customizer = context.getBean(CodecCustomizer.class); - DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer(); - customizer.customize(configurer); - DefaultPartHttpMessageReader partReader = getPartReader(configurer); - - // always set - assertThat(partReader).hasFieldOrPropertyWithValue("streaming", false); - assertThat(partReader).hasFieldOrPropertyWithValue("maxParts", 7); - assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize", - toIntBytes(DataSize.ofMegabytes(512))); - assertThat(partReader).hasFieldOrPropertyWithValue("headersCharset", StandardCharsets.UTF_16); - - // set when not streaming w/ limited memory size assertThat(partReader).hasFieldOrPropertyWithValue("maxInMemorySize", - toIntBytes(DataSize.ofGigabytes(1))); + Math.toIntExact(DataSize.ofGigabytes(1).toBytes())); assertThat(partReader).hasFieldOrPropertyWithValue("maxDiskUsagePerPart", DataSize.ofMegabytes(100).toBytes()); - assertThat(partReader).extracting("fileStorageDirectory") - .asInstanceOf(InstanceOfAssertFactories.type(Mono.class)) - .returns(Paths.get("."), Mono::block); }); } - @Test - void customizerSetsDefaultPropsWhenNoPropertiesAreSet() { - this.contextRunner.run((context) -> { - CodecCustomizer customizer = context.getBean(CodecCustomizer.class); - DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer(); - customizer.customize(configurer); - DefaultPartHttpMessageReader partReader = getPartReader(configurer); - assertThat(partReader).hasFieldOrPropertyWithValue("streaming", DEFAULT_STREAMING); - assertThat(partReader).hasFieldOrPropertyWithValue("maxParts", DEFAULT_MAX_PARTS); - assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize", DEFAULT_MAX_HEADERS_SIZE); - assertThat(partReader).hasFieldOrPropertyWithValue("headersCharset", DEFAULT_CHARSET); - assertThat(partReader).hasFieldOrPropertyWithValue("maxInMemorySize", DEFAULT_MAX_IN_MEMORY_SIZE); - assertThat(partReader).hasFieldOrPropertyWithValue("maxDiskUsagePerPart", DEFAULT_MAX_DISK_USAGE_PER_PART); - assertThat(partReader).extracting("fileStorageDirectory") - .asInstanceOf(InstanceOfAssertFactories.type(Mono.class)) - .returns(DEFAULT_FILE_STORAGE_DIRECTORY, Mono::block); - }); - } - - @Test - void customizerSetsDefaultPropsWhenDefaultsAreExplicitlySet() { - this.contextRunner.withPropertyValues("spring.webflux.multipart.streaming:false", - "spring.webflux.multipart.max-in-memory-size=256KB", "spring.webflux.multipart.max-headers-size=8KB", - "spring.webflux.multipart.max-disk-usage-per-part=-1", "spring.webflux.multipart.max-parts=-1", - "spring.webflux.multipart.file-storage-directory:" + DEFAULT_FILE_STORAGE_DIRECTORY, - "spring.webflux.multipart.headers-charset:" + DEFAULT_CHARSET).run((context) -> { - CodecCustomizer customizer = context.getBean(CodecCustomizer.class); - DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer(); - customizer.customize(configurer); - DefaultPartHttpMessageReader partReader = getPartReader(configurer); - assertThat(partReader).hasFieldOrPropertyWithValue("streaming", DEFAULT_STREAMING); - assertThat(partReader).hasFieldOrPropertyWithValue("maxParts", DEFAULT_MAX_PARTS); - assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize", DEFAULT_MAX_HEADERS_SIZE); - assertThat(partReader).hasFieldOrPropertyWithValue("headersCharset", DEFAULT_CHARSET); - assertThat(partReader).hasFieldOrPropertyWithValue("maxInMemorySize", DEFAULT_MAX_IN_MEMORY_SIZE); - assertThat(partReader).hasFieldOrPropertyWithValue("maxDiskUsagePerPart", - DEFAULT_MAX_DISK_USAGE_PER_PART); - assertThat(partReader).extracting("fileStorageDirectory") - .asInstanceOf(InstanceOfAssertFactories.type(Mono.class)) - .returns(DEFAULT_FILE_STORAGE_DIRECTORY, Mono::block); - }); - } - - @Test - void customizerBeanShouldHaveOrderOne() { - this.contextRunner.run((context) -> { - Method customizerMethod = ReflectionUtils.findMethod(ReactiveMultipartAutoConfiguration.class, - "defaultPartHttpMessageReaderCustomizer", ReactiveMultipartProperties.class); - Integer order = new TestAnnotationAwareOrderComparator().findOrder(customizerMethod); - assertThat(order).isEqualTo(1); - }); - } - private DefaultPartHttpMessageReader getPartReader(DefaultServerCodecConfigurer codecConfigurer) { return codecConfigurer.getReaders().stream().filter(DefaultPartHttpMessageReader.class::isInstance) .map(DefaultPartHttpMessageReader.class::cast).findFirst() .orElseThrow(() -> new IllegalStateException("Could not find DefaultPartHttpMessageReader")); } - private Integer toIntBytes(DataSize size) { - return (size != null) ? (int) size.toBytes() : null; - } - - static class TestAnnotationAwareOrderComparator extends AnnotationAwareOrderComparator { - - @Override - public Integer findOrder(Object obj) { - return super.findOrder(obj); - } - - } - }