Introduce BasicPropertyDescriptor to KotlinBeanInfoFactory to bypass early property type validation.

Closes #3167
This commit is contained in:
Mark Paluch
2024-09-30 09:23:19 +02:00
parent 4645ac3b97
commit 4ada6b9aaa
3 changed files with 96 additions and 2 deletions

View File

@@ -40,6 +40,7 @@ import org.springframework.core.KotlinDetector;
import org.springframework.core.Ordered;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@@ -112,8 +113,13 @@ public class KotlinBeanInfoFactory implements BeanInfoFactory, Ordered {
for (PropertyDescriptor descriptor : javaPropertyDescriptors) {
descriptor = new PropertyDescriptor(descriptor.getName(), specialize(beanClass, descriptor.getReadMethod()),
specialize(beanClass, descriptor.getWriteMethod()));
Method getter = specialize(beanClass, descriptor.getReadMethod());
Method setter = specialize(beanClass, descriptor.getWriteMethod());
if (!ObjectUtils.nullSafeEquals(descriptor.getReadMethod(), getter)
|| !ObjectUtils.nullSafeEquals(descriptor.getWriteMethod(), setter)) {
descriptor = new BasicPropertyDescriptor(descriptor.getName(), getter, setter);
}
descriptors.put(descriptor.getName(), descriptor);
}
}
@@ -148,4 +154,44 @@ public class KotlinBeanInfoFactory implements BeanInfoFactory, Ordered {
return LOWEST_PRECEDENCE - 10; // leave some space for customizations.
}
/**
* PropertyDescriptor for {@link KotlinBeanInfoFactory}, not performing any early type determination for
* {@link #setReadMethod}/{@link #setWriteMethod}.
*
* @since 3.3.5
*/
private static class BasicPropertyDescriptor extends PropertyDescriptor {
private @Nullable Method readMethod;
private @Nullable Method writeMethod;
public BasicPropertyDescriptor(String propertyName, @Nullable Method readMethod, @Nullable Method writeMethod)
throws IntrospectionException {
super(propertyName, readMethod, writeMethod);
}
@Override
public void setReadMethod(@Nullable Method readMethod) {
this.readMethod = readMethod;
}
@Override
@Nullable
public Method getReadMethod() {
return this.readMethod;
}
@Override
public void setWriteMethod(@Nullable Method writeMethod) {
this.writeMethod = writeMethod;
}
@Override
@Nullable
public Method getWriteMethod() {
return this.writeMethod;
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2024 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.data.util;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Optional;
import org.springframework.lang.Nullable;
public abstract class AbstractAuditable {
private @Nullable Date createdDate;
public Optional<LocalDateTime> getCreatedDate() {
return null == createdDate ? Optional.empty() : Optional.of(LocalDateTime.now());
}
public void setCreatedDate(LocalDateTime createdDate) {
this.createdDate = Date.from(createdDate.atZone(ZoneId.systemDefault()).toInstant());
}
}

View File

@@ -130,6 +130,13 @@ class KotlinBeanInfoFactoryUnitTests {
)
}
@Test // GH-3167
internal fun supportsPropertiesWithDifferentAccessorTypes() {
val pds = BeanUtils.getPropertyDescriptors(User::class.java)
assertThat(pds).isNotEmpty
}
data class SimpleDataClass(val id: String, var name: String)
@JvmInline
@@ -189,4 +196,8 @@ class KotlinBeanInfoFactoryUnitTests {
return super.getName()
}
}
class User : AbstractAuditable() {
var name: String? = null
}
}