public class CachedIntrospectionResults
extends java.lang.Object
PropertyDescriptor
information for a Java class. Not intended for direct use by application code.
Necessary for own caching of descriptors within the application's ClassLoader, rather than rely on the JDK's system-wide BeanInfo cache (in order to avoid leaks on ClassLoader shutdown).
Information is cached statically, so we don't need to create new
objects of this class for every JavaBean we manipulate. Hence, this class
implements the factory design pattern, using a private constructor and
a static 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
IntrospectorCleanupListener
in 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 IGNORE_BEANINFO_PROPERTY_NAME
system property.
acceptClassLoader(ClassLoader)
,
clearClassLoader(ClassLoader)
,
forClass(Class)
Modifier and Type | Field and Description |
---|---|
(package private) static java.util.Set<java.lang.ClassLoader> |
acceptedClassLoaders
Set of ClassLoaders that this CachedIntrospectionResults class will always
accept classes from, even if the classes do not qualify as cache-safe.
|
private java.beans.BeanInfo |
beanInfo
The BeanInfo object for the introspected bean class
|
private static java.util.List<BeanInfoFactory> |
beanInfoFactories
Stores the BeanInfoFactory instances
|
static java.lang.String |
IGNORE_BEANINFO_PROPERTY_NAME
System property that instructs Spring to use the
Introspector.IGNORE_ALL_BEANINFO
mode when calling the JavaBeans Introspector : "spring.beaninfo.ignore", with a
value of "true" skipping the search for BeanInfo classes (typically for scenarios
where no such classes are being defined for beans in the application in the first place). |
private static Log |
logger |
private java.util.Map<java.lang.String,java.beans.PropertyDescriptor> |
propertyDescriptorCache
PropertyDescriptor objects keyed by property name String
|
private static boolean |
shouldIntrospectorIgnoreBeaninfoClasses |
(package private) static java.util.concurrent.ConcurrentMap<java.lang.Class<?>,CachedIntrospectionResults> |
softClassCache
Map keyed by Class containing CachedIntrospectionResults, softly held.
|
(package private) static java.util.concurrent.ConcurrentMap<java.lang.Class<?>,CachedIntrospectionResults> |
strongClassCache
Map keyed by Class containing CachedIntrospectionResults, strongly held.
|
private java.util.concurrent.ConcurrentMap<java.beans.PropertyDescriptor,TypeDescriptor> |
typeDescriptorCache
TypeDescriptor objects keyed by PropertyDescriptor
|
Modifier | Constructor and Description |
---|---|
private |
CachedIntrospectionResults(java.lang.Class<?> beanClass)
Create a new CachedIntrospectionResults instance for the given class.
|
Modifier and Type | Method and Description |
---|---|
static void |
acceptClassLoader(java.lang.ClassLoader classLoader)
Accept the given ClassLoader as cache-safe, even if its classes would
not qualify as cache-safe in this CachedIntrospectionResults class.
|
(package private) TypeDescriptor |
addTypeDescriptor(java.beans.PropertyDescriptor pd,
TypeDescriptor td) |
private java.beans.PropertyDescriptor |
buildGenericTypeAwarePropertyDescriptor(java.lang.Class<?> beanClass,
java.beans.PropertyDescriptor pd) |
static void |
clearClassLoader(java.lang.ClassLoader classLoader)
Clear the introspection cache for the given ClassLoader, removing the
introspection results for all classes underneath that ClassLoader, and
removing the ClassLoader (and its children) from the acceptance list.
|
(package private) static CachedIntrospectionResults |
forClass(java.lang.Class<?> beanClass)
Create CachedIntrospectionResults for the given bean class.
|
(package private) java.lang.Class<?> |
getBeanClass() |
(package private) java.beans.BeanInfo |
getBeanInfo() |
(package private) java.beans.PropertyDescriptor |
getPropertyDescriptor(java.lang.String name) |
(package private) java.beans.PropertyDescriptor[] |
getPropertyDescriptors() |
(package private) TypeDescriptor |
getTypeDescriptor(java.beans.PropertyDescriptor pd) |
private static boolean |
isClassLoaderAccepted(java.lang.ClassLoader classLoader)
Check whether this CachedIntrospectionResults class is configured
to accept the given ClassLoader.
|
private static boolean |
isUnderneathClassLoader(java.lang.ClassLoader candidate,
java.lang.ClassLoader parent)
Check whether the given ClassLoader is underneath the given parent,
that is, whether the parent is within the candidate's hierarchy.
|
public static final java.lang.String IGNORE_BEANINFO_PROPERTY_NAME
Introspector.IGNORE_ALL_BEANINFO
mode when calling the JavaBeans Introspector
: "spring.beaninfo.ignore", with a
value of "true" skipping the search for BeanInfo
classes (typically for scenarios
where no such classes are being defined for beans in the application in the first place).
The default is "false", considering all BeanInfo
metadata classes, like for
standard Introspector.getBeanInfo(Class)
calls. Consider switching this flag to
"true" if you experience repeated ClassLoader access for non-existing 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
IntrospectorCleanupListener
in web.xml
in case of a multi-ClassLoader layout, which will allow for effective caching as well.
Introspector.getBeanInfo(Class, int)
,
Constant Field Valuesprivate static final boolean shouldIntrospectorIgnoreBeaninfoClasses
private static java.util.List<BeanInfoFactory> beanInfoFactories
private static final Log logger
static final java.util.Set<java.lang.ClassLoader> acceptedClassLoaders
static final java.util.concurrent.ConcurrentMap<java.lang.Class<?>,CachedIntrospectionResults> strongClassCache
static final java.util.concurrent.ConcurrentMap<java.lang.Class<?>,CachedIntrospectionResults> softClassCache
private final java.beans.BeanInfo beanInfo
private final java.util.Map<java.lang.String,java.beans.PropertyDescriptor> propertyDescriptorCache
private final java.util.concurrent.ConcurrentMap<java.beans.PropertyDescriptor,TypeDescriptor> typeDescriptorCache
private CachedIntrospectionResults(java.lang.Class<?> beanClass) throws BeansException
beanClass
- the bean class to analyzeBeansException
- in case of introspection failurepublic static void acceptClassLoader(@Nullable java.lang.ClassLoader classLoader)
This configuration method is only relevant in scenarios where the Spring classes reside in a 'common' ClassLoader (e.g. the system ClassLoader) whose lifecycle is not coupled to the application. In such a scenario, CachedIntrospectionResults would by default not cache any of the application's classes, since they would create a leak in the common ClassLoader.
Any acceptClassLoader
call at application startup should
be paired with a clearClassLoader(java.lang.ClassLoader)
call at application shutdown.
classLoader
- the ClassLoader to acceptpublic static void clearClassLoader(@Nullable java.lang.ClassLoader classLoader)
classLoader
- the ClassLoader to clear the cache forstatic CachedIntrospectionResults forClass(java.lang.Class<?> beanClass) throws BeansException
beanClass
- the bean class to analyzeBeansException
- in case of introspection failureprivate static boolean isClassLoaderAccepted(java.lang.ClassLoader classLoader)
classLoader
- the ClassLoader to checkacceptClassLoader(java.lang.ClassLoader)
private static boolean isUnderneathClassLoader(@Nullable java.lang.ClassLoader candidate, @Nullable java.lang.ClassLoader parent)
candidate
- the candidate ClassLoader to checkparent
- the parent ClassLoader to check forjava.beans.BeanInfo getBeanInfo()
java.lang.Class<?> getBeanClass()
@Nullable java.beans.PropertyDescriptor getPropertyDescriptor(java.lang.String name)
java.beans.PropertyDescriptor[] getPropertyDescriptors()
private java.beans.PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(java.lang.Class<?> beanClass, java.beans.PropertyDescriptor pd)
TypeDescriptor addTypeDescriptor(java.beans.PropertyDescriptor pd, TypeDescriptor td)
@Nullable TypeDescriptor getTypeDescriptor(java.beans.PropertyDescriptor pd)