Skip to content

Commit

Permalink
CachedIntrospectionResults only caches GenericTypeAwarePropertyDescri…
Browse files Browse the repository at this point in the history
…ptors if fully safe (SPR-7227)
  • Loading branch information
jhoeller committed May 27, 2010
1 parent dea5918 commit 3a5af35
Showing 1 changed file with 37 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@
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;
Expand Down Expand Up @@ -141,19 +140,20 @@ static CachedIntrospectionResults forClass(Class beanClass) throws BeansExceptio
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<CachedIntrospectionResults>(results));
}
}
Expand Down Expand Up @@ -216,7 +216,7 @@ private static boolean isUnderneathClassLoader(ClassLoader candidate, ClassLoade
* @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() + "]");
Expand All @@ -237,24 +237,29 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]");
}
this.propertyDescriptorCache = new HashMap<String, PropertyDescriptor>();
this.propertyDescriptorCache = new LinkedHashMap<String, PropertyDescriptor>();

// 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);
}
}

Expand All @@ -275,12 +280,29 @@ PropertyDescriptor getPropertyDescriptor(String name) {
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<PropertyDescriptor> 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);
}
}

}

0 comments on commit 3a5af35

Please sign in to comment.