Skip to content

Commit 4ada6b9

Browse files
committed
Introduce BasicPropertyDescriptor to KotlinBeanInfoFactory to bypass early property type validation.
Closes #3167
1 parent 4645ac3 commit 4ada6b9

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

src/main/java/org/springframework/data/util/KotlinBeanInfoFactory.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.core.Ordered;
4141
import org.springframework.lang.Nullable;
4242
import org.springframework.util.ClassUtils;
43+
import org.springframework.util.ObjectUtils;
4344
import org.springframework.util.ReflectionUtils;
4445
import org.springframework.util.StringUtils;
4546

@@ -112,8 +113,13 @@ public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
112113

113114
for (PropertyDescriptor descriptor : javaPropertyDescriptors) {
114115

115-
descriptor = new PropertyDescriptor(descriptor.getName(), specialize(beanClass, descriptor.getReadMethod()),
116-
specialize(beanClass, descriptor.getWriteMethod()));
116+
Method getter = specialize(beanClass, descriptor.getReadMethod());
117+
Method setter = specialize(beanClass, descriptor.getWriteMethod());
118+
119+
if (!ObjectUtils.nullSafeEquals(descriptor.getReadMethod(), getter)
120+
|| !ObjectUtils.nullSafeEquals(descriptor.getWriteMethod(), setter)) {
121+
descriptor = new BasicPropertyDescriptor(descriptor.getName(), getter, setter);
122+
}
117123
descriptors.put(descriptor.getName(), descriptor);
118124
}
119125
}
@@ -148,4 +154,44 @@ public int getOrder() {
148154
return LOWEST_PRECEDENCE - 10; // leave some space for customizations.
149155
}
150156

157+
/**
158+
* PropertyDescriptor for {@link KotlinBeanInfoFactory}, not performing any early type determination for
159+
* {@link #setReadMethod}/{@link #setWriteMethod}.
160+
*
161+
* @since 3.3.5
162+
*/
163+
private static class BasicPropertyDescriptor extends PropertyDescriptor {
164+
165+
private @Nullable Method readMethod;
166+
167+
private @Nullable Method writeMethod;
168+
169+
public BasicPropertyDescriptor(String propertyName, @Nullable Method readMethod, @Nullable Method writeMethod)
170+
throws IntrospectionException {
171+
172+
super(propertyName, readMethod, writeMethod);
173+
}
174+
175+
@Override
176+
public void setReadMethod(@Nullable Method readMethod) {
177+
this.readMethod = readMethod;
178+
}
179+
180+
@Override
181+
@Nullable
182+
public Method getReadMethod() {
183+
return this.readMethod;
184+
}
185+
186+
@Override
187+
public void setWriteMethod(@Nullable Method writeMethod) {
188+
this.writeMethod = writeMethod;
189+
}
190+
191+
@Override
192+
@Nullable
193+
public Method getWriteMethod() {
194+
return this.writeMethod;
195+
}
196+
}
151197
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.util;
17+
18+
import java.time.LocalDateTime;
19+
import java.time.ZoneId;
20+
import java.util.Date;
21+
import java.util.Optional;
22+
23+
import org.springframework.lang.Nullable;
24+
25+
public abstract class AbstractAuditable {
26+
27+
private @Nullable Date createdDate;
28+
29+
public Optional<LocalDateTime> getCreatedDate() {
30+
return null == createdDate ? Optional.empty() : Optional.of(LocalDateTime.now());
31+
}
32+
33+
public void setCreatedDate(LocalDateTime createdDate) {
34+
this.createdDate = Date.from(createdDate.atZone(ZoneId.systemDefault()).toInstant());
35+
}
36+
37+
}

src/test/kotlin/org/springframework/data/util/KotlinBeanInfoFactoryUnitTests.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ class KotlinBeanInfoFactoryUnitTests {
130130
)
131131
}
132132

133+
@Test // GH-3167
134+
internal fun supportsPropertiesWithDifferentAccessorTypes() {
135+
136+
val pds = BeanUtils.getPropertyDescriptors(User::class.java)
137+
assertThat(pds).isNotEmpty
138+
}
139+
133140
data class SimpleDataClass(val id: String, var name: String)
134141

135142
@JvmInline
@@ -189,4 +196,8 @@ class KotlinBeanInfoFactoryUnitTests {
189196
return super.getName()
190197
}
191198
}
199+
200+
class User : AbstractAuditable() {
201+
var name: String? = null
202+
}
192203
}

0 commit comments

Comments
 (0)