Merge branch '5.3.x'
This commit is contained in:
commit
c3b29960ed
|
@ -41,15 +41,21 @@ import org.springframework.util.ReflectionUtils;
|
|||
* <h3>Conversion Algorithm</h3>
|
||||
* <ol>
|
||||
* <li>Invoke a non-static {@code to[targetType.simpleName]()} method on the
|
||||
* source object that has a return type equal to {@code targetType}, if such
|
||||
* source object that has a return type assignable to {@code targetType}, if such
|
||||
* a method exists. For example, {@code org.example.Bar Foo#toBar()} is a
|
||||
* method that follows this convention.
|
||||
* <li>Otherwise invoke a <em>static</em> {@code valueOf(sourceType)} or Java
|
||||
* 8 style <em>static</em> {@code of(sourceType)} or {@code from(sourceType)}
|
||||
* method on the {@code targetType}, if such a method exists.
|
||||
* method on the {@code targetType} that has a return type <em>related</em> to
|
||||
* {@code targetType}, if such a method exists. For example, a static
|
||||
* {@code Foo.of(sourceType)} method that returns a {@code Foo},
|
||||
* {@code SuperFooType}, or {@code SubFooType} is a method that follows this
|
||||
* convention. {@link java.time.ZoneId#of(String)} is a concrete example of
|
||||
* such a static factory method which returns a subtype of {@code ZoneId}.
|
||||
* <li>Otherwise invoke a constructor on the {@code targetType} that accepts
|
||||
* a single {@code sourceType} argument, if such a constructor exists.
|
||||
* <li>Otherwise throw a {@link ConversionFailedException}.
|
||||
* <li>Otherwise throw a {@link ConversionFailedException} or
|
||||
* {@link IllegalStateException}.
|
||||
* </ol>
|
||||
*
|
||||
* <p><strong>Warning</strong>: this converter does <em>not</em> support the
|
||||
|
@ -189,7 +195,18 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
|
|||
method = ClassUtils.getStaticMethod(targetClass, "from", sourceClass);
|
||||
}
|
||||
}
|
||||
return method;
|
||||
|
||||
return (method != null && areRelatedTypes(targetClass, method.getReturnType()) ? method : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the two types reside in the same type hierarchy (i.e., type 1
|
||||
* is assignable to type 2 or vice versa).
|
||||
* @since 5.3.21
|
||||
* @see ClassUtils#isAssignable(Class, Class)
|
||||
*/
|
||||
private static boolean areRelatedTypes(Class<?> type1, Class<?> type2) {
|
||||
return (ClassUtils.isAssignable(type1, type2) || ClassUtils.isAssignable(type2, type1));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -824,6 +824,9 @@ class DefaultConversionServiceTests {
|
|||
assertThat(ISBN.toStringCount).as("toString() invocations").isEqualTo(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.core.convert.support.ObjectToObjectConverterTests
|
||||
*/
|
||||
@Test
|
||||
void convertObjectToObjectUsingValueOfMethod() {
|
||||
ISBN.reset();
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.core.convert.support;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.convert.ConverterNotFoundException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ObjectToObjectConverter}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Phil Webb
|
||||
* @since 5.3.21
|
||||
* @see org.springframework.core.convert.converter.DefaultConversionServiceTests#convertObjectToObjectUsingValueOfMethod()
|
||||
*/
|
||||
class ObjectToObjectConverterTests {
|
||||
|
||||
private final GenericConversionService conversionService = new GenericConversionService() {{
|
||||
addConverter(new ObjectToObjectConverter());
|
||||
}};
|
||||
|
||||
|
||||
/**
|
||||
* This test effectively verifies that the {@link ObjectToObjectConverter}
|
||||
* was properly registered with the {@link GenericConversionService}.
|
||||
*/
|
||||
@Test
|
||||
void nonStaticToTargetTypeSimpleNameMethodWithMatchingReturnType() {
|
||||
assertThat(conversionService.canConvert(Source.class, Data.class))
|
||||
.as("can convert Source to Data").isTrue();
|
||||
Data data = conversionService.convert(new Source("test"), Data.class);
|
||||
assertThat(data).asString().isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonStaticToTargetTypeSimpleNameMethodWithDifferentReturnType() {
|
||||
assertThat(conversionService.canConvert(Text.class, Data.class))
|
||||
.as("can convert Text to Data").isFalse();
|
||||
assertThat(conversionService.canConvert(Text.class, Optional.class))
|
||||
.as("can convert Text to Optional").isFalse();
|
||||
assertThatExceptionOfType(ConverterNotFoundException.class)
|
||||
.as("convert Text to Data")
|
||||
.isThrownBy(() -> conversionService.convert(new Text("test"), Data.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void staticValueOfFactoryMethodWithDifferentReturnType() {
|
||||
assertThat(conversionService.canConvert(String.class, Data.class))
|
||||
.as("can convert String to Data").isFalse();
|
||||
assertThatExceptionOfType(ConverterNotFoundException.class)
|
||||
.as("convert String to Data")
|
||||
.isThrownBy(() -> conversionService.convert("test", Data.class));
|
||||
}
|
||||
|
||||
|
||||
static class Source {
|
||||
|
||||
private final String value;
|
||||
|
||||
private Source(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Data toData() {
|
||||
return new Data(this.value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Text {
|
||||
|
||||
private final String value;
|
||||
|
||||
private Text(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Optional<Data> toData() {
|
||||
return Optional.of(new Data(this.value));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Data {
|
||||
|
||||
private final String value;
|
||||
|
||||
private Data(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static Optional<Data> valueOf(String string) {
|
||||
return (string != null) ? Optional.of(new Data(string)) : Optional.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue