Skip to content

Commit 3a5af35

Browse files
committed
CachedIntrospectionResults only caches GenericTypeAwarePropertyDescriptors if fully safe (SPR-7227)
1 parent dea5918 commit 3a5af35

File tree

1 file changed

+37
-15
lines changed

1 file changed

+37
-15
lines changed

org.springframework.beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,10 @@
2222
import java.beans.PropertyDescriptor;
2323
import java.lang.ref.Reference;
2424
import java.lang.ref.WeakReference;
25-
import java.util.Collection;
2625
import java.util.Collections;
27-
import java.util.HashMap;
2826
import java.util.HashSet;
2927
import java.util.Iterator;
28+
import java.util.LinkedHashMap;
3029
import java.util.Map;
3130
import java.util.Set;
3231
import java.util.WeakHashMap;
@@ -141,19 +140,20 @@ static CachedIntrospectionResults forClass(Class beanClass) throws BeansExceptio
141140
results = (CachedIntrospectionResults) value;
142141
}
143142
if (results == null) {
144-
// can throw BeansException
145-
results = new CachedIntrospectionResults(beanClass);
146143
// On JDK 1.5 and higher, it is almost always safe to cache the bean class...
147144
// The sole exception is a custom BeanInfo class being provided in a non-safe ClassLoader.
148-
if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
149-
isClassLoaderAccepted(beanClass.getClassLoader()) ||
150-
!ClassUtils.isPresent(beanClass.getName() + "BeanInfo", beanClass.getClassLoader())) {
145+
boolean fullyCacheable =
146+
ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
147+
isClassLoaderAccepted(beanClass.getClassLoader());
148+
if (fullyCacheable || !ClassUtils.isPresent(beanClass.getName() + "BeanInfo", beanClass.getClassLoader())) {
149+
results = new CachedIntrospectionResults(beanClass, fullyCacheable);
151150
classCache.put(beanClass, results);
152151
}
153152
else {
154153
if (logger.isDebugEnabled()) {
155154
logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
156155
}
156+
results = new CachedIntrospectionResults(beanClass, true);
157157
classCache.put(beanClass, new WeakReference<CachedIntrospectionResults>(results));
158158
}
159159
}
@@ -216,7 +216,7 @@ private static boolean isUnderneathClassLoader(ClassLoader candidate, ClassLoade
216216
* @param beanClass the bean class to analyze
217217
* @throws BeansException in case of introspection failure
218218
*/
219-
private CachedIntrospectionResults(Class beanClass) throws BeansException {
219+
private CachedIntrospectionResults(Class beanClass, boolean cacheFullMetadata) throws BeansException {
220220
try {
221221
if (logger.isTraceEnabled()) {
222222
logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]");
@@ -237,24 +237,29 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException {
237237
if (logger.isTraceEnabled()) {
238238
logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]");
239239
}
240-
this.propertyDescriptorCache = new HashMap<String, PropertyDescriptor>();
240+
this.propertyDescriptorCache = new LinkedHashMap<String, PropertyDescriptor>();
241241

242242
// This call is slow so we do it once.
243243
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
244244
for (PropertyDescriptor pd : pds) {
245+
if (Class.class.equals(beanClass) && "classLoader".equals(pd.getName())) {
246+
// Ignore Class.getClassLoader() method - nobody needs to bind to that
247+
continue;
248+
}
245249
if (logger.isTraceEnabled()) {
246250
logger.trace("Found bean property '" + pd.getName() + "'" +
247251
(pd.getPropertyType() != null ? " of type [" + pd.getPropertyType().getName() + "]" : "") +
248252
(pd.getPropertyEditorClass() != null ?
249253
"; editor [" + pd.getPropertyEditorClass().getName() + "]" : ""));
250254
}
251-
pd = new GenericTypeAwarePropertyDescriptor(beanClass, pd.getName(), pd.getReadMethod(),
252-
pd.getWriteMethod(), pd.getPropertyEditorClass());
255+
if (cacheFullMetadata) {
256+
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
257+
}
253258
this.propertyDescriptorCache.put(pd.getName(), pd);
254259
}
255260
}
256261
catch (IntrospectionException ex) {
257-
throw new FatalBeanException("Cannot get BeanInfo for object of class [" + beanClass.getName() + "]", ex);
262+
throw new FatalBeanException("Failed to obtain BeanInfo for class [" + beanClass.getName() + "]", ex);
258263
}
259264
}
260265

@@ -275,12 +280,29 @@ PropertyDescriptor getPropertyDescriptor(String name) {
275280
pd = this.propertyDescriptorCache.get(name.substring(0, 1).toUpperCase() + name.substring(1));
276281
}
277282
}
278-
return pd;
283+
return (pd == null || pd instanceof GenericTypeAwarePropertyDescriptor ? pd :
284+
buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd));
279285
}
280286

281287
PropertyDescriptor[] getPropertyDescriptors() {
282-
Collection<PropertyDescriptor> descriptorCollection = this.propertyDescriptorCache.values();
283-
return descriptorCollection.toArray(new PropertyDescriptor[descriptorCollection.size()]);
288+
PropertyDescriptor[] pds = new PropertyDescriptor[this.propertyDescriptorCache.size()];
289+
int i = 0;
290+
for (PropertyDescriptor pd : this.propertyDescriptorCache.values()) {
291+
pds[i] = (pd instanceof GenericTypeAwarePropertyDescriptor ? pd :
292+
buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd));
293+
i++;
294+
}
295+
return pds;
296+
}
297+
298+
private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class beanClass, PropertyDescriptor pd) {
299+
try {
300+
return new GenericTypeAwarePropertyDescriptor(beanClass, pd.getName(), pd.getReadMethod(),
301+
pd.getWriteMethod(), pd.getPropertyEditorClass());
302+
}
303+
catch (IntrospectionException ex) {
304+
throw new FatalBeanException("Failed to re-introspect class [" + beanClass.getName() + "]", ex);
305+
}
284306
}
285307

286308
}

0 commit comments

Comments
 (0)