diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java index ea0b8d2aceb8..52782a3d7ed6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -194,7 +194,6 @@ public static T instantiateClass(Constructor ctor, Object... args) throws * @since 5.0 * @see Kotlin docs */ - @SuppressWarnings("unchecked") @Nullable public static Constructor findPrimaryConstructor(Class clazz) { Assert.notNull(clazz, "Class must not be null"); @@ -409,8 +408,7 @@ else if (startParen == -1) { * @throws BeansException if PropertyDescriptor look fails */ public static PropertyDescriptor[] getPropertyDescriptors(Class clazz) throws BeansException { - CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); - return cr.getPropertyDescriptors(); + return CachedIntrospectionResults.forClass(clazz).getPropertyDescriptors(); } /** @@ -421,11 +419,8 @@ public static PropertyDescriptor[] getPropertyDescriptors(Class clazz) throws * @throws BeansException if PropertyDescriptor lookup fails */ @Nullable - public static PropertyDescriptor getPropertyDescriptor(Class clazz, String propertyName) - throws BeansException { - - CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); - return cr.getPropertyDescriptor(propertyName); + public static PropertyDescriptor getPropertyDescriptor(Class clazz, String propertyName) throws BeansException { + return CachedIntrospectionResults.forClass(clazz).getPropertyDescriptor(propertyName); } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java index 7d94fe28bd2e..63ab95a11fe9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -92,6 +92,8 @@ public final class CachedIntrospectionResults { */ public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore"; + private static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {}; + private static final boolean shouldIntrospectorIgnoreBeaninfoClasses = SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME); @@ -253,7 +255,7 @@ private static BeanInfo getBeanInfo(Class beanClass) throws IntrospectionExce private final BeanInfo beanInfo; /** PropertyDescriptor objects keyed by property name String. */ - private final Map propertyDescriptorCache; + private final Map propertyDescriptors; /** TypeDescriptor objects keyed by PropertyDescriptor. */ private final ConcurrentMap typeDescriptorCache; @@ -274,7 +276,7 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException { if (logger.isTraceEnabled()) { 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. PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors(); @@ -291,7 +293,7 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException { "; editor [" + pd.getPropertyEditorClass().getName() + "]" : "")); } 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, @@ -313,13 +315,13 @@ private void introspectInterfaces(Class beanClass, Class currClass) throws for (Class ifc : currClass.getInterfaces()) { if (!ClassUtils.isJavaLanguageInterface(ifc)) { for (PropertyDescriptor pd : getBeanInfo(ifc).getPropertyDescriptors()) { - PropertyDescriptor existingPd = this.propertyDescriptorCache.get(pd.getName()); + PropertyDescriptor existingPd = this.propertyDescriptors.get(pd.getName()); if (existingPd == null || (existingPd.getReadMethod() == null && pd.getReadMethod() != null)) { // GenericTypeAwarePropertyDescriptor leniently resolves a set* write method // against a declared read method, so we prefer read method descriptors here. pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd); - this.propertyDescriptorCache.put(pd.getName(), pd); + this.propertyDescriptors.put(pd.getName(), pd); } } introspectInterfaces(ifc, ifc); @@ -338,27 +340,19 @@ Class getBeanClass() { @Nullable PropertyDescriptor getPropertyDescriptor(String name) { - PropertyDescriptor pd = this.propertyDescriptorCache.get(name); + PropertyDescriptor pd = this.propertyDescriptors.get(name); if (pd == null && StringUtils.hasLength(name)) { // Same lenient fallback checking as in Property... - pd = this.propertyDescriptorCache.get(StringUtils.uncapitalize(name)); + pd = this.propertyDescriptors.get(StringUtils.uncapitalize(name)); if (pd == null) { - pd = this.propertyDescriptorCache.get(StringUtils.capitalize(name)); + pd = this.propertyDescriptors.get(StringUtils.capitalize(name)); } } - return (pd == null || pd instanceof GenericTypeAwarePropertyDescriptor ? pd : - buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd)); + return pd; } PropertyDescriptor[] getPropertyDescriptors() { - PropertyDescriptor[] pds = new PropertyDescriptor[this.propertyDescriptorCache.size()]; - int i = 0; - for (PropertyDescriptor pd : this.propertyDescriptorCache.values()) { - pds[i] = (pd instanceof GenericTypeAwarePropertyDescriptor ? pd : - buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd)); - i++; - } - return pds; + return this.propertyDescriptors.values().toArray(EMPTY_PROPERTY_DESCRIPTOR_ARRAY); } private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class beanClass, PropertyDescriptor pd) { diff --git a/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java index c58cd665613e..399fb1e33350 100644 --- a/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java +++ b/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java @@ -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"); * you may not use this file except in compliance with the License. @@ -60,12 +60,13 @@ final class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor { @Nullable private Class propertyType; + @Nullable private final Class propertyEditorClass; public GenericTypeAwarePropertyDescriptor(Class beanClass, String propertyName, - @Nullable Method readMethod, @Nullable Method writeMethod, Class propertyEditorClass) - throws IntrospectionException { + @Nullable Method readMethod, @Nullable Method writeMethod, + @Nullable Class propertyEditorClass) throws IntrospectionException { super(propertyName, null, null); this.beanClass = beanClass; @@ -157,6 +158,7 @@ public Class getPropertyType() { } @Override + @Nullable public Class getPropertyEditorClass() { return this.propertyEditorClass; } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java index c041af56b4e1..67e718c0193d 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -114,8 +114,6 @@ public BeanPropertyRowMapper() { /** * Create a new {@code BeanPropertyRowMapper}, accepting unpopulated * properties in the target bean. - *

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 */ public BeanPropertyRowMapper(Class mappedClass) { @@ -222,8 +220,8 @@ protected void initialize(Class mappedClass) { this.mappedClass = mappedClass; this.mappedFields = new HashMap<>(); this.mappedProperties = new HashSet<>(); - PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass); - for (PropertyDescriptor pd : pds) { + + for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(mappedClass)) { if (pd.getWriteMethod() != null) { this.mappedFields.put(lowerCaseName(pd.getName()), pd); String underscoredName = underscoreName(pd.getName()); @@ -247,6 +245,7 @@ protected String underscoreName(String name) { if (!StringUtils.hasLength(name)) { return ""; } + StringBuilder result = new StringBuilder(); result.append(lowerCaseName(name.substring(0, 1))); for (int i = 1; i < name.length(); i++) { @@ -337,8 +336,7 @@ public T mapRow(ResultSet rs, int rowNumber) throws SQLException { if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) { throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields " + - "necessary to populate object of class [" + this.mappedClass.getName() + "]: " + - this.mappedProperties); + "necessary to populate object of " + this.mappedClass + ": " + this.mappedProperties); } return mappedObject; @@ -380,8 +378,7 @@ protected Object getColumnValue(ResultSet rs, int index, PropertyDescriptor pd) /** - * Static factory method to create a new {@code BeanPropertyRowMapper} - * (with the mapped class specified only once). + * Static factory method to create a new {@code BeanPropertyRowMapper}. * @param mappedClass the class that each row should be mapped to */ public static BeanPropertyRowMapper newInstance(Class mappedClass) { diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/SingleColumnRowMapper.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/SingleColumnRowMapper.java index 0a2e9cb491c9..3b762906d807 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/SingleColumnRowMapper.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/SingleColumnRowMapper.java @@ -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"); * you may not use this file except in compliance with the License. @@ -62,8 +62,6 @@ public SingleColumnRowMapper() { /** * Create a new {@code SingleColumnRowMapper}. - *

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 */ public SingleColumnRowMapper(Class requiredType) { @@ -216,8 +214,7 @@ else if (this.conversionService != null && this.conversionService.canConvert(val /** - * Static factory method to create a new {@code SingleColumnRowMapper} - * (with the required type specified only once). + * Static factory method to create a new {@code SingleColumnRowMapper}. * @param requiredType the type that each result object is expected to match * @since 4.1 * @see #newInstance(Class, ConversionService) @@ -227,8 +224,7 @@ public static SingleColumnRowMapper newInstance(Class requiredType) { } /** - * Static factory method to create a new {@code SingleColumnRowMapper} - * (with the required type specified only once). + * Static factory method to create a new {@code SingleColumnRowMapper}. * @param requiredType the type that each result object is expected to match * @param conversionService the {@link ConversionService} for converting a * fetched value, or {@code null} for none diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java index 1d8df5fe7fc0..8763d3bd0a02 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2020 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. @@ -43,7 +43,7 @@ public class BeanPropertyRowMapperTests extends AbstractRowMapperTests { @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) public void testOverridingDifferentClassDefinedForMapping() { BeanPropertyRowMapper mapper = new BeanPropertyRowMapper(Person.class); thrown.expect(InvalidDataAccessApiUsageException.class);