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 44d076680d..f99f920b20 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -50,6 +50,18 @@ import org.springframework.util.StringUtils; * implements the factory design pattern, using a private constructor and * a static {@link #forClass(Class)} factory method to obtain instances. * + *

Note that for caching to work effectively, some preconditions need to be met: + * Prefer an arrangement where the Spring jars live in the same ClassLoader as the + * application classes, which allows for clean caching along with the application's + * lifecycle in any case. For a web application, consider declaring a local + * {@link org.springframework.web.util.IntrospectorCleanupListener} in {@code web.xml} + * in case of a multi-ClassLoader layout, which will allow for effective caching as well. + * + *

In case of a non-clean ClassLoader arrangement without a cleanup listener having + * been set up, this class will fall back to a weak-reference-based caching model that + * recreates much-requested entries every time the garbage collector removed them. In + * such a scenario, consider the {@link #IGNORE_BEANINFO_PROPERTY_NAME} system property. + * * @author Rod Johnson * @author Juergen Hoeller * @since 05 May 2001 @@ -59,11 +71,33 @@ import org.springframework.util.StringUtils; */ public class CachedIntrospectionResults { + /** + * System property that instructs Spring to use the {@link Introspector#IGNORE_ALL_BEANINFO} + * mode when calling the JavaBeans {@link Introspector}: "spring.beaninfo.ignore", with a + * value of "true" skipping the search for {@code BeanInfo} classes (typically for scenarios + * where no such classes are being defined for beans in the application in the first place). + *

Default is "false", considering all {@code BeanInfo} metadata classes, like for + * standard {@link Introspector#getBeanInfo(Class)} calls. Consider switching this flag to + * "true" if you experience repeated ClassLoader access for non-existing {@code BeanInfo} + * classes, in case such access is expensive on startup or on lazy loading. + *

Note that such an effect may also indicate a scenario where caching doesn't work + * effectively: Prefer an arrangement where the Spring jars live in the same ClassLoader + * as the application classes, which allows for clean caching along with the application's + * lifecycle in any case. For a web application, consider declaring a local + * {@link org.springframework.web.util.IntrospectorCleanupListener} in {@code web.xml} + * in case of a multi-ClassLoader layout, which will allow for effective caching as well. + * @see Introspector#getBeanInfo(Class, int) + */ + public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore"; + + private static final Log logger = LogFactory.getLog(CachedIntrospectionResults.class); + private static final boolean shouldIntrospectorIgnoreBeaninfoClasses; + /** Stores the BeanInfoFactory instances */ - private static List beanInfoFactories = - SpringFactoriesLoader.loadFactories(BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader()); + private static List beanInfoFactories = SpringFactoriesLoader.loadFactories( + BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader()); /** * Set of ClassLoaders that this CachedIntrospectionResults class will always @@ -79,6 +113,21 @@ public class CachedIntrospectionResults { static final Map, Object> classCache = new WeakHashMap, Object>(); + static { + boolean ignoreValue; + try { + ignoreValue = "true".equalsIgnoreCase(System.getProperty(IGNORE_BEANINFO_PROPERTY_NAME)); + } + catch (Throwable ex) { + if (logger.isDebugEnabled()) { + logger.debug("Could not obtain system property '" + IGNORE_BEANINFO_PROPERTY_NAME + "': " + ex); + } + ignoreValue = false; + } + shouldIntrospectorIgnoreBeaninfoClasses = ignoreValue; + } + + /** * Accept the given ClassLoader as cache-safe, even if its classes would * not qualify as cache-safe in this CachedIntrospectionResults class. @@ -238,7 +287,9 @@ public class CachedIntrospectionResults { } if (beanInfo == null) { // If none of the factories supported the class, fall back to the default - beanInfo = Introspector.getBeanInfo(beanClass); + beanInfo = (shouldIntrospectorIgnoreBeaninfoClasses ? + Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) : + Introspector.getBeanInfo(beanClass)); } this.beanInfo = beanInfo;