2020import java .beans .IntrospectionException ;
2121import java .beans .Introspector ;
2222import java .beans .PropertyDescriptor ;
23- import java .lang .ref .Reference ;
24- import java .lang .ref .WeakReference ;
25- import java .util .HashSet ;
23+ import java .util .Collections ;
2624import java .util .Iterator ;
2725import java .util .LinkedHashMap ;
2826import java .util .List ;
2927import java .util .Map ;
3028import java .util .Set ;
31- import java .util .WeakHashMap ;
3229import java .util .concurrent .ConcurrentHashMap ;
3330
3431import org .apache .commons .logging .Log ;
3835import org .springframework .core .convert .TypeDescriptor ;
3936import org .springframework .core .io .support .SpringFactoriesLoader ;
4037import org .springframework .util .ClassUtils ;
38+ import org .springframework .util .ConcurrentReferenceHashMap ;
4139import org .springframework .util .StringUtils ;
4240
4341/**
@@ -107,14 +105,22 @@ public class CachedIntrospectionResults {
107105 * Set of ClassLoaders that this CachedIntrospectionResults class will always
108106 * accept classes from, even if the classes do not qualify as cache-safe.
109107 */
110- static final Set <ClassLoader > acceptedClassLoaders = new HashSet <ClassLoader >();
108+ static final Set <ClassLoader > acceptedClassLoaders =
109+ Collections .newSetFromMap (new ConcurrentHashMap <ClassLoader , Boolean >(16 ));
111110
112111 /**
113- * Map keyed by class containing CachedIntrospectionResults.
114- * Needs to be a WeakHashMap with WeakReferences as values to allow
115- * for proper garbage collection in case of multiple class loaders.
112+ * Map keyed by Class containing CachedIntrospectionResults, strongly held.
113+ * This variant is being used for cache-safe bean classes.
116114 */
117- static final Map <Class <?>, Object > classCache = new WeakHashMap <Class <?>, Object >();
115+ static final Map <Class <?>, CachedIntrospectionResults > strongClassCache =
116+ new ConcurrentHashMap <Class <?>, CachedIntrospectionResults >(64 );
117+
118+ /**
119+ * Map keyed by Class containing CachedIntrospectionResults, softly held.
120+ * This variant is being used for non-cache-safe bean classes.
121+ */
122+ static final Map <Class <?>, CachedIntrospectionResults > softClassCache =
123+ new ConcurrentReferenceHashMap <Class <?>, CachedIntrospectionResults >(64 );
118124
119125
120126 /**
@@ -131,9 +137,7 @@ public class CachedIntrospectionResults {
131137 */
132138 public static void acceptClassLoader (ClassLoader classLoader ) {
133139 if (classLoader != null ) {
134- synchronized (acceptedClassLoaders ) {
135- acceptedClassLoaders .add (classLoader );
136- }
140+ acceptedClassLoaders .add (classLoader );
137141 }
138142 }
139143
@@ -144,20 +148,22 @@ public static void acceptClassLoader(ClassLoader classLoader) {
144148 * @param classLoader the ClassLoader to clear the cache for
145149 */
146150 public static void clearClassLoader (ClassLoader classLoader ) {
147- synchronized (classCache ) {
148- for (Iterator <Class <?>> it = classCache .keySet ().iterator (); it .hasNext ();) {
149- Class <?> beanClass = it .next ();
150- if (isUnderneathClassLoader (beanClass .getClassLoader (), classLoader )) {
151- it .remove ();
152- }
151+ for (Iterator <ClassLoader > it = acceptedClassLoaders .iterator (); it .hasNext ();) {
152+ ClassLoader registeredLoader = it .next ();
153+ if (isUnderneathClassLoader (registeredLoader , classLoader )) {
154+ it .remove ();
153155 }
154156 }
155- synchronized (acceptedClassLoaders ) {
156- for (Iterator <ClassLoader > it = acceptedClassLoaders .iterator (); it .hasNext ();) {
157- ClassLoader registeredLoader = it .next ();
158- if (isUnderneathClassLoader (registeredLoader , classLoader )) {
159- it .remove ();
160- }
157+ for (Iterator <Class <?>> it = strongClassCache .keySet ().iterator (); it .hasNext ();) {
158+ Class <?> beanClass = it .next ();
159+ if (isUnderneathClassLoader (beanClass .getClassLoader (), classLoader )) {
160+ it .remove ();
161+ }
162+ }
163+ for (Iterator <Class <?>> it = softClassCache .keySet ().iterator (); it .hasNext ();) {
164+ Class <?> beanClass = it .next ();
165+ if (isUnderneathClassLoader (beanClass .getClassLoader (), classLoader )) {
166+ it .remove ();
161167 }
162168 }
163169 }
@@ -170,35 +176,25 @@ public static void clearClassLoader(ClassLoader classLoader) {
170176 */
171177 @ SuppressWarnings ("unchecked" )
172178 static CachedIntrospectionResults forClass (Class <?> beanClass ) throws BeansException {
173- CachedIntrospectionResults results ;
174- Object value ;
175- synchronized (classCache ) {
176- value = classCache .get (beanClass );
179+ CachedIntrospectionResults results = strongClassCache .get (beanClass );
180+ if (results != null ) {
181+ return results ;
177182 }
178- if ( value instanceof Reference ) {
179- Reference < CachedIntrospectionResults > ref = ( Reference < CachedIntrospectionResults >) value ;
180- results = ref . get () ;
183+ results = softClassCache . get ( beanClass );
184+ if ( results != null ) {
185+ return results ;
181186 }
182- else {
183- results = (CachedIntrospectionResults ) value ;
187+
188+ results = new CachedIntrospectionResults (beanClass );
189+ if (ClassUtils .isCacheSafe (beanClass , CachedIntrospectionResults .class .getClassLoader ()) ||
190+ isClassLoaderAccepted (beanClass .getClassLoader ())) {
191+ strongClassCache .put (beanClass , results );
184192 }
185- if (results == null ) {
186- if (ClassUtils .isCacheSafe (beanClass , CachedIntrospectionResults .class .getClassLoader ()) ||
187- isClassLoaderAccepted (beanClass .getClassLoader ())) {
188- results = new CachedIntrospectionResults (beanClass );
189- synchronized (classCache ) {
190- classCache .put (beanClass , results );
191- }
192- }
193- else {
194- if (logger .isDebugEnabled ()) {
195- logger .debug ("Not strongly caching class [" + beanClass .getName () + "] because it is not cache-safe" );
196- }
197- results = new CachedIntrospectionResults (beanClass );
198- synchronized (classCache ) {
199- classCache .put (beanClass , new WeakReference <CachedIntrospectionResults >(results ));
200- }
193+ else {
194+ if (logger .isDebugEnabled ()) {
195+ logger .debug ("Not strongly caching class [" + beanClass .getName () + "] because it is not cache-safe" );
201196 }
197+ softClassCache .put (beanClass , results );
202198 }
203199 return results ;
204200 }
@@ -211,13 +207,7 @@ static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansExcep
211207 * @see #acceptClassLoader
212208 */
213209 private static boolean isClassLoaderAccepted (ClassLoader classLoader ) {
214- // Iterate over array copy in order to avoid synchronization for the entire
215- // ClassLoader check (avoiding a synchronized acceptedClassLoaders Iterator).
216- ClassLoader [] acceptedLoaderArray ;
217- synchronized (acceptedClassLoaders ) {
218- acceptedLoaderArray = acceptedClassLoaders .toArray (new ClassLoader [acceptedClassLoaders .size ()]);
219- }
220- for (ClassLoader acceptedLoader : acceptedLoaderArray ) {
210+ for (ClassLoader acceptedLoader : acceptedClassLoaders ) {
221211 if (isUnderneathClassLoader (classLoader , acceptedLoader )) {
222212 return true ;
223213 }
0 commit comments