diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/org.springframework.beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java index e69cebdb65f..569c317b304 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -22,11 +22,10 @@ import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.ref.Reference; import java.lang.ref.WeakReference; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; @@ -141,19 +140,20 @@ public class CachedIntrospectionResults { results = (CachedIntrospectionResults) value; } if (results == null) { - // can throw BeansException - results = new CachedIntrospectionResults(beanClass); // On JDK 1.5 and higher, it is almost always safe to cache the bean class... // The sole exception is a custom BeanInfo class being provided in a non-safe ClassLoader. - if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) || - isClassLoaderAccepted(beanClass.getClassLoader()) || - !ClassUtils.isPresent(beanClass.getName() + "BeanInfo", beanClass.getClassLoader())) { + boolean fullyCacheable = + ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) || + isClassLoaderAccepted(beanClass.getClassLoader()); + if (fullyCacheable || !ClassUtils.isPresent(beanClass.getName() + "BeanInfo", beanClass.getClassLoader())) { + results = new CachedIntrospectionResults(beanClass, fullyCacheable); classCache.put(beanClass, results); } else { if (logger.isDebugEnabled()) { logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe"); } + results = new CachedIntrospectionResults(beanClass, true); classCache.put(beanClass, new WeakReference(results)); } } @@ -216,7 +216,7 @@ public class CachedIntrospectionResults { * @param beanClass the bean class to analyze * @throws BeansException in case of introspection failure */ - private CachedIntrospectionResults(Class beanClass) throws BeansException { + private CachedIntrospectionResults(Class beanClass, boolean cacheFullMetadata) throws BeansException { try { if (logger.isTraceEnabled()) { logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]"); @@ -237,24 +237,29 @@ public class CachedIntrospectionResults { if (logger.isTraceEnabled()) { logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]"); } - this.propertyDescriptorCache = new HashMap(); + this.propertyDescriptorCache = new LinkedHashMap(); // This call is slow so we do it once. PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors(); for (PropertyDescriptor pd : pds) { + if (Class.class.equals(beanClass) && "classLoader".equals(pd.getName())) { + // Ignore Class.getClassLoader() method - nobody needs to bind to that + continue; + } if (logger.isTraceEnabled()) { logger.trace("Found bean property '" + pd.getName() + "'" + (pd.getPropertyType() != null ? " of type [" + pd.getPropertyType().getName() + "]" : "") + (pd.getPropertyEditorClass() != null ? "; editor [" + pd.getPropertyEditorClass().getName() + "]" : "")); } - pd = new GenericTypeAwarePropertyDescriptor(beanClass, pd.getName(), pd.getReadMethod(), - pd.getWriteMethod(), pd.getPropertyEditorClass()); + if (cacheFullMetadata) { + pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd); + } this.propertyDescriptorCache.put(pd.getName(), pd); } } catch (IntrospectionException ex) { - throw new FatalBeanException("Cannot get BeanInfo for object of class [" + beanClass.getName() + "]", ex); + throw new FatalBeanException("Failed to obtain BeanInfo for class [" + beanClass.getName() + "]", ex); } } @@ -275,12 +280,29 @@ public class CachedIntrospectionResults { pd = this.propertyDescriptorCache.get(name.substring(0, 1).toUpperCase() + name.substring(1)); } } - return pd; + return (pd == null || pd instanceof GenericTypeAwarePropertyDescriptor ? pd : + buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd)); } PropertyDescriptor[] getPropertyDescriptors() { - Collection descriptorCollection = this.propertyDescriptorCache.values(); - return descriptorCollection.toArray(new PropertyDescriptor[descriptorCollection.size()]); + 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; + } + + private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class beanClass, PropertyDescriptor pd) { + try { + return new GenericTypeAwarePropertyDescriptor(beanClass, pd.getName(), pd.getReadMethod(), + pd.getWriteMethod(), pd.getPropertyEditorClass()); + } + catch (IntrospectionException ex) { + throw new FatalBeanException("Failed to re-introspect class [" + beanClass.getName() + "]", ex); + } } }