Refine CachedIntrospectionResults property introspection

Closes gh-28445
This commit is contained in:
Juergen Hoeller 2022-05-11 08:32:06 +02:00
parent dc2947c52d
commit 83186b689f
2 changed files with 36 additions and 11 deletions

View File

@ -22,6 +22,7 @@ import java.beans.Introspector;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.net.URL;
import java.security.ProtectionDomain; import java.security.ProtectionDomain;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -292,10 +293,12 @@ public final class CachedIntrospectionResults {
// Only allow all name variants of Class properties // Only allow all name variants of Class properties
continue; continue;
} }
if (pd.getWriteMethod() == null && pd.getPropertyType() != null && if (URL.class == beanClass && "content".equals(pd.getName())) {
(ClassLoader.class.isAssignableFrom(pd.getPropertyType()) || // Only allow URL attribute introspection, not content resolution
ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) { continue;
// 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; continue;
} }
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
@ -344,10 +347,8 @@ public final class CachedIntrospectionResults {
// 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);
if (pd.getWriteMethod() == null && pd.getPropertyType() != null && if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
(ClassLoader.class.isAssignableFrom(pd.getPropertyType()) || // Ignore read-only properties such as ClassLoader - no need to bind to those
ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) {
// Ignore ClassLoader and ProtectionDomain read-only properties - no need to bind to those
continue; continue;
} }
this.propertyDescriptors.put(pd.getName(), pd); this.propertyDescriptors.put(pd.getName(), pd);
@ -379,8 +380,7 @@ public final class CachedIntrospectionResults {
if (Modifier.isStatic(method.getModifiers()) || if (Modifier.isStatic(method.getModifiers()) ||
method.getDeclaringClass() == Object.class || method.getDeclaringClass() == Class.class || method.getDeclaringClass() == Object.class || method.getDeclaringClass() == Class.class ||
method.getParameterCount() > 0 || method.getReturnType() == void.class || method.getParameterCount() > 0 || method.getReturnType() == void.class ||
ClassLoader.class.isAssignableFrom(method.getReturnType()) || isInvalidReadOnlyPropertyType(method.getReturnType())) {
ProtectionDomain.class.isAssignableFrom(method.getReturnType())) {
return false; return false;
} }
try { try {
@ -393,6 +393,12 @@ public final class CachedIntrospectionResults {
} }
} }
private boolean isInvalidReadOnlyPropertyType(@Nullable Class<?> returnType) {
return (returnType != null && (AutoCloseable.class.isAssignableFrom(returnType) ||
ClassLoader.class.isAssignableFrom(returnType) ||
ProtectionDomain.class.isAssignableFrom(returnType)));
}
BeanInfo getBeanInfo() { BeanInfo getBeanInfo() {
return this.beanInfo; return this.beanInfo;

View File

@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.OverridingClassLoader; import org.springframework.core.OverridingClassLoader;
import org.springframework.core.io.DefaultResourceLoader; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@ -153,7 +154,7 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
} }
@Test @Test
void propertyDescriptors() { void propertyDescriptors() throws Exception {
TestBean target = new TestBean(); TestBean target = new TestBean();
target.setSpouse(new TestBean()); target.setSpouse(new TestBean());
BeanWrapper accessor = createAccessor(target); BeanWrapper accessor = createAccessor(target);
@ -182,11 +183,29 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
assertThat(accessor.isReadableProperty("class.package")).isFalse(); assertThat(accessor.isReadableProperty("class.package")).isFalse();
assertThat(accessor.isReadableProperty("class.module")).isFalse(); assertThat(accessor.isReadableProperty("class.module")).isFalse();
assertThat(accessor.isReadableProperty("class.classLoader")).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.isReadableProperty("classLoader")).isTrue();
assertThat(accessor.isWritableProperty("classLoader")).isTrue(); assertThat(accessor.isWritableProperty("classLoader")).isTrue();
OverridingClassLoader ocl = new OverridingClassLoader(getClass().getClassLoader()); OverridingClassLoader ocl = new OverridingClassLoader(getClass().getClassLoader());
accessor.setPropertyValue("classLoader", ocl); accessor.setPropertyValue("classLoader", ocl);
assertThat(accessor.getPropertyValue("classLoader")).isSameAs(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 @Test