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 73165cc0cb8..42e9acca27f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -295,7 +295,7 @@ public final class CachedIntrospectionResults { // Only allow URL attribute introspection, not content resolution continue; } - if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) { + if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) { // Ignore read-only properties such as ClassLoader - no need to bind to those continue; } @@ -345,7 +345,8 @@ public final class CachedIntrospectionResults { // 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 && isInvalidReadOnlyPropertyType(pd.getPropertyType())) { + if (pd.getWriteMethod() == null && + isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) { // Ignore read-only properties such as ClassLoader - no need to bind to those continue; } @@ -378,7 +379,7 @@ public final class CachedIntrospectionResults { if (Modifier.isStatic(method.getModifiers()) || method.getDeclaringClass() == Object.class || method.getDeclaringClass() == Class.class || method.getParameterCount() > 0 || method.getReturnType() == void.class || - isInvalidReadOnlyPropertyType(method.getReturnType())) { + isInvalidReadOnlyPropertyType(method.getReturnType(), method.getDeclaringClass())) { return false; } try { @@ -391,10 +392,11 @@ 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))); + private boolean isInvalidReadOnlyPropertyType(@Nullable Class returnType, Class beanClass) { + return (returnType != null && (ClassLoader.class.isAssignableFrom(returnType) || + ProtectionDomain.class.isAssignableFrom(returnType) || + (AutoCloseable.class.isAssignableFrom(returnType) && + !AutoCloseable.class.isAssignableFrom(beanClass)))); } 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 30ab8b1716d..17a775a7f12 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java @@ -216,6 +216,10 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests { assertThat(accessor.isReadableProperty("inputStream")).isFalse(); assertThat(accessor.isReadableProperty("filename")).isTrue(); assertThat(accessor.isReadableProperty("description")).isTrue(); + + accessor = createAccessor(new ActiveResource()); + + assertThat(accessor.isReadableProperty("resource")).isTrue(); } @Test @@ -376,6 +380,18 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests { } + public static class ActiveResource implements AutoCloseable { + + public ActiveResource getResource() { + return this; + } + + @Override + public void close() throws Exception { + } + } + + public static class GetterWithOptional { public TestBean value;