From 50177b1ad3485bd44239b1756f6c14607476fcf2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 11 May 2022 08:32:06 +0200 Subject: [PATCH] Refine CachedIntrospectionResults property introspection Closes gh-28445 --- .../beans/CachedIntrospectionResults.java | 23 ++++++++++++------- .../beans/BeanWrapperTests.java | 21 ++++++++++++++++- 2 files changed, 35 insertions(+), 9 deletions(-) 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 c25a12b91195..e669b202ac6c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -20,6 +20,7 @@ import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; +import java.net.URL; import java.security.ProtectionDomain; import java.util.Collections; import java.util.LinkedHashMap; @@ -287,10 +288,12 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException { // Only allow all name variants of Class properties continue; } - if (pd.getWriteMethod() == null && pd.getPropertyType() != null && - (ClassLoader.class.isAssignableFrom(pd.getPropertyType()) || - ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) { - // Ignore ClassLoader and ProtectionDomain read-only properties - no need to bind to those + if (URL.class == beanClass && "content".equals(pd.getName())) { + // Only allow URL attribute introspection, not content resolution + continue; + } + if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) { + // Ignore read-only properties such as ClassLoader - no need to bind to those continue; } if (logger.isTraceEnabled()) { @@ -328,10 +331,8 @@ private void introspectInterfaces(Class beanClass, Class currClass) throws // GenericTypeAwarePropertyDescriptor leniently resolves a set* write method // against a declared read method, so we prefer read method descriptors here. pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd); - if (pd.getWriteMethod() == null && pd.getPropertyType() != null && - (ClassLoader.class.isAssignableFrom(pd.getPropertyType()) || - ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) { - // Ignore ClassLoader and ProtectionDomain read-only properties - no need to bind to those + if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) { + // Ignore read-only properties such as ClassLoader - no need to bind to those continue; } this.propertyDescriptors.put(pd.getName(), pd); @@ -342,6 +343,12 @@ private void introspectInterfaces(Class beanClass, Class currClass) throws } } + private boolean isInvalidReadOnlyPropertyType(@Nullable Class returnType) { + return (returnType != null && (AutoCloseable.class.isAssignableFrom(returnType) || + ClassLoader.class.isAssignableFrom(returnType) || + ProtectionDomain.class.isAssignableFrom(returnType))); + } + BeanInfo getBeanInfo() { return this.beanInfo; diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java index 691e954603f5..7a7b71db91f2 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java @@ -25,6 +25,7 @@ import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.OverridingClassLoader; import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.UrlResource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -152,7 +153,7 @@ public void setPropertyTypeMismatch() { } @Test - public void propertyDescriptors() { + public void propertyDescriptors() throws Exception { TestBean target = new TestBean(); target.setSpouse(new TestBean()); BeanWrapper accessor = createAccessor(target); @@ -181,11 +182,29 @@ public void propertyDescriptors() { assertThat(accessor.isReadableProperty("class.package")).isFalse(); assertThat(accessor.isReadableProperty("class.module")).isFalse(); assertThat(accessor.isReadableProperty("class.classLoader")).isFalse(); + assertThat(accessor.isReadableProperty("class.name")).isTrue(); + assertThat(accessor.isReadableProperty("class.simpleName")).isTrue(); assertThat(accessor.isReadableProperty("classLoader")).isTrue(); assertThat(accessor.isWritableProperty("classLoader")).isTrue(); OverridingClassLoader ocl = new OverridingClassLoader(getClass().getClassLoader()); accessor.setPropertyValue("classLoader", ocl); assertThat(accessor.getPropertyValue("classLoader")).isSameAs(ocl); + + accessor = createAccessor(new UrlResource("https://spring.io")); + + assertThat(accessor.isReadableProperty("class.package")).isFalse(); + assertThat(accessor.isReadableProperty("class.module")).isFalse(); + assertThat(accessor.isReadableProperty("class.classLoader")).isFalse(); + assertThat(accessor.isReadableProperty("class.name")).isTrue(); + assertThat(accessor.isReadableProperty("class.simpleName")).isTrue(); + assertThat(accessor.isReadableProperty("URL.protocol")).isTrue(); + assertThat(accessor.isReadableProperty("URL.host")).isTrue(); + assertThat(accessor.isReadableProperty("URL.port")).isTrue(); + assertThat(accessor.isReadableProperty("URL.file")).isTrue(); + assertThat(accessor.isReadableProperty("URL.content")).isFalse(); + assertThat(accessor.isReadableProperty("inputStream")).isFalse(); + assertThat(accessor.isReadableProperty("filename")).isTrue(); + assertThat(accessor.isReadableProperty("description")).isTrue(); } @Test