Polishing
This commit is contained in:
@@ -227,7 +227,6 @@ public abstract class BeanUtils {
|
|||||||
* @since 5.0
|
* @since 5.0
|
||||||
* @see <a href="https://kotlinlang.org/docs/reference/classes.html#constructors">Kotlin docs</a>
|
* @see <a href="https://kotlinlang.org/docs/reference/classes.html#constructors">Kotlin docs</a>
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
|
public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
|
||||||
Assert.notNull(clazz, "Class must not be null");
|
Assert.notNull(clazz, "Class must not be null");
|
||||||
@@ -442,8 +441,7 @@ public abstract class BeanUtils {
|
|||||||
* @throws BeansException if PropertyDescriptor look fails
|
* @throws BeansException if PropertyDescriptor look fails
|
||||||
*/
|
*/
|
||||||
public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException {
|
public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException {
|
||||||
CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
|
return CachedIntrospectionResults.forClass(clazz).getPropertyDescriptors();
|
||||||
return cr.getPropertyDescriptors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -454,11 +452,8 @@ public abstract class BeanUtils {
|
|||||||
* @throws BeansException if PropertyDescriptor lookup fails
|
* @throws BeansException if PropertyDescriptor lookup fails
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName)
|
public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName) throws BeansException {
|
||||||
throws BeansException {
|
return CachedIntrospectionResults.forClass(clazz).getPropertyDescriptor(propertyName);
|
||||||
|
|
||||||
CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
|
|
||||||
return cr.getPropertyDescriptor(propertyName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ public final class CachedIntrospectionResults {
|
|||||||
*/
|
*/
|
||||||
public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";
|
public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";
|
||||||
|
|
||||||
|
private static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {};
|
||||||
|
|
||||||
|
|
||||||
private static final boolean shouldIntrospectorIgnoreBeaninfoClasses =
|
private static final boolean shouldIntrospectorIgnoreBeaninfoClasses =
|
||||||
SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME);
|
SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME);
|
||||||
@@ -253,7 +255,7 @@ public final class CachedIntrospectionResults {
|
|||||||
private final BeanInfo beanInfo;
|
private final BeanInfo beanInfo;
|
||||||
|
|
||||||
/** PropertyDescriptor objects keyed by property name String. */
|
/** PropertyDescriptor objects keyed by property name String. */
|
||||||
private final Map<String, PropertyDescriptor> propertyDescriptorCache;
|
private final Map<String, PropertyDescriptor> propertyDescriptors;
|
||||||
|
|
||||||
/** TypeDescriptor objects keyed by PropertyDescriptor. */
|
/** TypeDescriptor objects keyed by PropertyDescriptor. */
|
||||||
private final ConcurrentMap<PropertyDescriptor, TypeDescriptor> typeDescriptorCache;
|
private final ConcurrentMap<PropertyDescriptor, TypeDescriptor> typeDescriptorCache;
|
||||||
@@ -274,7 +276,7 @@ public final class CachedIntrospectionResults {
|
|||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]");
|
logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]");
|
||||||
}
|
}
|
||||||
this.propertyDescriptorCache = new LinkedHashMap<>();
|
this.propertyDescriptors = new LinkedHashMap<>();
|
||||||
|
|
||||||
// This call is slow so we do it once.
|
// This call is slow so we do it once.
|
||||||
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
|
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
|
||||||
@@ -291,7 +293,7 @@ public final class CachedIntrospectionResults {
|
|||||||
"; editor [" + pd.getPropertyEditorClass().getName() + "]" : ""));
|
"; editor [" + pd.getPropertyEditorClass().getName() + "]" : ""));
|
||||||
}
|
}
|
||||||
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
||||||
this.propertyDescriptorCache.put(pd.getName(), pd);
|
this.propertyDescriptors.put(pd.getName(), pd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicitly check implemented interfaces for setter/getter methods as well,
|
// Explicitly check implemented interfaces for setter/getter methods as well,
|
||||||
@@ -313,13 +315,13 @@ public final class CachedIntrospectionResults {
|
|||||||
for (Class<?> ifc : currClass.getInterfaces()) {
|
for (Class<?> ifc : currClass.getInterfaces()) {
|
||||||
if (!ClassUtils.isJavaLanguageInterface(ifc)) {
|
if (!ClassUtils.isJavaLanguageInterface(ifc)) {
|
||||||
for (PropertyDescriptor pd : getBeanInfo(ifc).getPropertyDescriptors()) {
|
for (PropertyDescriptor pd : getBeanInfo(ifc).getPropertyDescriptors()) {
|
||||||
PropertyDescriptor existingPd = this.propertyDescriptorCache.get(pd.getName());
|
PropertyDescriptor existingPd = this.propertyDescriptors.get(pd.getName());
|
||||||
if (existingPd == null ||
|
if (existingPd == null ||
|
||||||
(existingPd.getReadMethod() == null && pd.getReadMethod() != null)) {
|
(existingPd.getReadMethod() == null && pd.getReadMethod() != null)) {
|
||||||
// GenericTypeAwarePropertyDescriptor leniently resolves a set* write method
|
// GenericTypeAwarePropertyDescriptor leniently resolves a set* write method
|
||||||
// against a declared read method, so we prefer read method descriptors here.
|
// against a declared read method, so we prefer read method descriptors here.
|
||||||
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
||||||
this.propertyDescriptorCache.put(pd.getName(), pd);
|
this.propertyDescriptors.put(pd.getName(), pd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
introspectInterfaces(ifc, ifc);
|
introspectInterfaces(ifc, ifc);
|
||||||
@@ -338,27 +340,19 @@ public final class CachedIntrospectionResults {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
PropertyDescriptor getPropertyDescriptor(String name) {
|
PropertyDescriptor getPropertyDescriptor(String name) {
|
||||||
PropertyDescriptor pd = this.propertyDescriptorCache.get(name);
|
PropertyDescriptor pd = this.propertyDescriptors.get(name);
|
||||||
if (pd == null && StringUtils.hasLength(name)) {
|
if (pd == null && StringUtils.hasLength(name)) {
|
||||||
// Same lenient fallback checking as in Property...
|
// Same lenient fallback checking as in Property...
|
||||||
pd = this.propertyDescriptorCache.get(StringUtils.uncapitalize(name));
|
pd = this.propertyDescriptors.get(StringUtils.uncapitalize(name));
|
||||||
if (pd == null) {
|
if (pd == null) {
|
||||||
pd = this.propertyDescriptorCache.get(StringUtils.capitalize(name));
|
pd = this.propertyDescriptors.get(StringUtils.capitalize(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (pd == null || pd instanceof GenericTypeAwarePropertyDescriptor ? pd :
|
return pd;
|
||||||
buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyDescriptor[] getPropertyDescriptors() {
|
PropertyDescriptor[] getPropertyDescriptors() {
|
||||||
PropertyDescriptor[] pds = new PropertyDescriptor[this.propertyDescriptorCache.size()];
|
return this.propertyDescriptors.values().toArray(EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
|
||||||
int i = 0;
|
|
||||||
for (PropertyDescriptor pd : this.propertyDescriptorCache.values()) {
|
|
||||||
pds[i] = (pd instanceof GenericTypeAwarePropertyDescriptor ? pd :
|
|
||||||
buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return pds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class<?> beanClass, PropertyDescriptor pd) {
|
private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class<?> beanClass, PropertyDescriptor pd) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -60,12 +60,13 @@ final class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private Class<?> propertyType;
|
private Class<?> propertyType;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private final Class<?> propertyEditorClass;
|
private final Class<?> propertyEditorClass;
|
||||||
|
|
||||||
|
|
||||||
public GenericTypeAwarePropertyDescriptor(Class<?> beanClass, String propertyName,
|
public GenericTypeAwarePropertyDescriptor(Class<?> beanClass, String propertyName,
|
||||||
@Nullable Method readMethod, @Nullable Method writeMethod, Class<?> propertyEditorClass)
|
@Nullable Method readMethod, @Nullable Method writeMethod,
|
||||||
throws IntrospectionException {
|
@Nullable Class<?> propertyEditorClass) throws IntrospectionException {
|
||||||
|
|
||||||
super(propertyName, null, null);
|
super(propertyName, null, null);
|
||||||
this.beanClass = beanClass;
|
this.beanClass = beanClass;
|
||||||
@@ -156,6 +157,7 @@ final class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Nullable
|
||||||
public Class<?> getPropertyEditorClass() {
|
public Class<?> getPropertyEditorClass() {
|
||||||
return this.propertyEditorClass;
|
return this.propertyEditorClass;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -114,8 +114,6 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
|||||||
/**
|
/**
|
||||||
* Create a new {@code BeanPropertyRowMapper}, accepting unpopulated
|
* Create a new {@code BeanPropertyRowMapper}, accepting unpopulated
|
||||||
* properties in the target bean.
|
* properties in the target bean.
|
||||||
* <p>Consider using the {@link #newInstance} factory method instead,
|
|
||||||
* which allows for specifying the mapped type once only.
|
|
||||||
* @param mappedClass the class that each row should be mapped to
|
* @param mappedClass the class that each row should be mapped to
|
||||||
*/
|
*/
|
||||||
public BeanPropertyRowMapper(Class<T> mappedClass) {
|
public BeanPropertyRowMapper(Class<T> mappedClass) {
|
||||||
@@ -222,8 +220,8 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
|||||||
this.mappedClass = mappedClass;
|
this.mappedClass = mappedClass;
|
||||||
this.mappedFields = new HashMap<>();
|
this.mappedFields = new HashMap<>();
|
||||||
this.mappedProperties = new HashSet<>();
|
this.mappedProperties = new HashSet<>();
|
||||||
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
|
|
||||||
for (PropertyDescriptor pd : pds) {
|
for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(mappedClass)) {
|
||||||
if (pd.getWriteMethod() != null) {
|
if (pd.getWriteMethod() != null) {
|
||||||
this.mappedFields.put(lowerCaseName(pd.getName()), pd);
|
this.mappedFields.put(lowerCaseName(pd.getName()), pd);
|
||||||
String underscoredName = underscoreName(pd.getName());
|
String underscoredName = underscoreName(pd.getName());
|
||||||
@@ -247,6 +245,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
|||||||
if (!StringUtils.hasLength(name)) {
|
if (!StringUtils.hasLength(name)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
result.append(lowerCaseName(name.substring(0, 1)));
|
result.append(lowerCaseName(name.substring(0, 1)));
|
||||||
for (int i = 1; i < name.length(); i++) {
|
for (int i = 1; i < name.length(); i++) {
|
||||||
@@ -337,8 +336,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
|||||||
|
|
||||||
if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) {
|
if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) {
|
||||||
throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields " +
|
throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields " +
|
||||||
"necessary to populate object of class [" + this.mappedClass.getName() + "]: " +
|
"necessary to populate object of " + this.mappedClass + ": " + this.mappedProperties);
|
||||||
this.mappedProperties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mappedObject;
|
return mappedObject;
|
||||||
@@ -380,8 +378,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static factory method to create a new {@code BeanPropertyRowMapper}
|
* Static factory method to create a new {@code BeanPropertyRowMapper}.
|
||||||
* (with the mapped class specified only once).
|
|
||||||
* @param mappedClass the class that each row should be mapped to
|
* @param mappedClass the class that each row should be mapped to
|
||||||
* @see #newInstance(Class, ConversionService)
|
* @see #newInstance(Class, ConversionService)
|
||||||
*/
|
*/
|
||||||
@@ -390,8 +387,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static factory method to create a new {@code BeanPropertyRowMapper}
|
* Static factory method to create a new {@code BeanPropertyRowMapper}.
|
||||||
* (with the required type specified only once).
|
|
||||||
* @param mappedClass the class that each row should be mapped to
|
* @param mappedClass the class that each row should be mapped to
|
||||||
* @param conversionService the {@link ConversionService} for binding
|
* @param conversionService the {@link ConversionService} for binding
|
||||||
* JDBC values to bean properties, or {@code null} for none
|
* JDBC values to bean properties, or {@code null} for none
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -62,8 +62,6 @@ public class SingleColumnRowMapper<T> implements RowMapper<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@code SingleColumnRowMapper}.
|
* Create a new {@code SingleColumnRowMapper}.
|
||||||
* <p>Consider using the {@link #newInstance} factory method instead,
|
|
||||||
* which allows for specifying the required type once only.
|
|
||||||
* @param requiredType the type that each result object is expected to match
|
* @param requiredType the type that each result object is expected to match
|
||||||
*/
|
*/
|
||||||
public SingleColumnRowMapper(Class<T> requiredType) {
|
public SingleColumnRowMapper(Class<T> requiredType) {
|
||||||
@@ -216,8 +214,7 @@ public class SingleColumnRowMapper<T> implements RowMapper<T> {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static factory method to create a new {@code SingleColumnRowMapper}
|
* Static factory method to create a new {@code SingleColumnRowMapper}.
|
||||||
* (with the required type specified only once).
|
|
||||||
* @param requiredType the type that each result object is expected to match
|
* @param requiredType the type that each result object is expected to match
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
* @see #newInstance(Class, ConversionService)
|
* @see #newInstance(Class, ConversionService)
|
||||||
@@ -227,8 +224,7 @@ public class SingleColumnRowMapper<T> implements RowMapper<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static factory method to create a new {@code SingleColumnRowMapper}
|
* Static factory method to create a new {@code SingleColumnRowMapper}.
|
||||||
* (with the required type specified only once).
|
|
||||||
* @param requiredType the type that each result object is expected to match
|
* @param requiredType the type that each result object is expected to match
|
||||||
* @param conversionService the {@link ConversionService} for converting a
|
* @param conversionService the {@link ConversionService} for converting a
|
||||||
* fetched value, or {@code null} for none
|
* fetched value, or {@code null} for none
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -37,9 +37,8 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|||||||
*/
|
*/
|
||||||
public class BeanPropertyRowMapperTests extends AbstractRowMapperTests {
|
public class BeanPropertyRowMapperTests extends AbstractRowMapperTests {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public void testOverridingDifferentClassDefinedForMapping() {
|
public void testOverridingDifferentClassDefinedForMapping() {
|
||||||
BeanPropertyRowMapper mapper = new BeanPropertyRowMapper(Person.class);
|
BeanPropertyRowMapper mapper = new BeanPropertyRowMapper(Person.class);
|
||||||
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(() ->
|
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(() ->
|
||||||
|
|||||||
Reference in New Issue
Block a user