From ce2f28da49d1e300d81589d70b38d39b450aefee Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 27 Apr 2016 15:39:00 +0200 Subject: [PATCH] CachedIntrospectionResults explicitly introspects implemented interfaces (for Java 8 default methods) Issue: SPR-14198 --- .../beans/CachedIntrospectionResults.java | 20 ++++++++++++- .../beans/BeanWrapperTests.java | 30 +++++++++++++++++-- 2 files changed, 47 insertions(+), 3 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 41c2f4c576..2efe9dabeb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -303,6 +303,24 @@ public class CachedIntrospectionResults { this.propertyDescriptorCache.put(pd.getName(), pd); } + // Explicitly check implemented interfaces for setter/getter methods as well, + // in particular for Java 8 default methods... + Class clazz = beanClass; + while (clazz != null) { + Class[] ifcs = clazz.getInterfaces(); + for (Class ifc : ifcs) { + BeanInfo ifcInfo = Introspector.getBeanInfo(ifc, Introspector.IGNORE_ALL_BEANINFO); + PropertyDescriptor[] ifcPds = ifcInfo.getPropertyDescriptors(); + for (PropertyDescriptor pd : ifcPds) { + if (!this.propertyDescriptorCache.containsKey(pd.getName())) { + pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd); + this.propertyDescriptorCache.put(pd.getName(), pd); + } + } + } + clazz = clazz.getSuperclass(); + } + this.typeDescriptorCache = new ConcurrentReferenceHashMap(); } catch (IntrospectionException ex) { 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 7a3232b1b1..9d79f7f7ac 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -61,6 +61,14 @@ public class BeanWrapperTests extends AbstractPropertyAccessorTests { assertTrue("Set name to tom", target.getName().equals("tom")); } + @Test + public void aliasedSetterThroughDefaultMethod() { + GetterBean target = new GetterBean(); + BeanWrapper accessor = createAccessor(target); + accessor.setPropertyValue("aliasedName", "tom"); + assertTrue("Set name to tom", target.getAliasedName().equals("tom")); + } + @Test public void setValidAndInvalidPropertyValuesShouldContainExceptionDetails() { TestBean target = new TestBean(); @@ -196,7 +204,24 @@ public class BeanWrapperTests extends AbstractPropertyAccessorTests { @SuppressWarnings("unused") - private static class GetterBean { + private interface AliasedProperty { + + default void setAliasedName(String name) { + setName(name); + } + + default String getAliasedName() { + return getName(); + } + + void setName(String name); + + String getName(); + } + + + @SuppressWarnings("unused") + private static class GetterBean implements AliasedProperty { private String name; @@ -212,6 +237,7 @@ public class BeanWrapperTests extends AbstractPropertyAccessorTests { } } + @SuppressWarnings("unused") private static class IntelliBean {