20
20
import java .beans .IntrospectionException ;
21
21
import java .beans .Introspector ;
22
22
import 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 ;
26
24
import java .util .Iterator ;
27
25
import java .util .LinkedHashMap ;
28
26
import java .util .List ;
29
27
import java .util .Map ;
30
28
import java .util .Set ;
31
- import java .util .WeakHashMap ;
32
29
import java .util .concurrent .ConcurrentHashMap ;
33
30
34
31
import org .apache .commons .logging .Log ;
38
35
import org .springframework .core .convert .TypeDescriptor ;
39
36
import org .springframework .core .io .support .SpringFactoriesLoader ;
40
37
import org .springframework .util .ClassUtils ;
38
+ import org .springframework .util .ConcurrentReferenceHashMap ;
41
39
import org .springframework .util .StringUtils ;
42
40
43
41
/**
@@ -107,14 +105,22 @@ public class CachedIntrospectionResults {
107
105
* Set of ClassLoaders that this CachedIntrospectionResults class will always
108
106
* accept classes from, even if the classes do not qualify as cache-safe.
109
107
*/
110
- static final Set <ClassLoader > acceptedClassLoaders = new HashSet <ClassLoader >();
108
+ static final Set <ClassLoader > acceptedClassLoaders =
109
+ Collections .newSetFromMap (new ConcurrentHashMap <ClassLoader , Boolean >(16 ));
111
110
112
111
/**
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.
116
114
*/
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 );
118
124
119
125
120
126
/**
@@ -131,9 +137,7 @@ public class CachedIntrospectionResults {
131
137
*/
132
138
public static void acceptClassLoader (ClassLoader classLoader ) {
133
139
if (classLoader != null ) {
134
- synchronized (acceptedClassLoaders ) {
135
- acceptedClassLoaders .add (classLoader );
136
- }
140
+ acceptedClassLoaders .add (classLoader );
137
141
}
138
142
}
139
143
@@ -144,20 +148,22 @@ public static void acceptClassLoader(ClassLoader classLoader) {
144
148
* @param classLoader the ClassLoader to clear the cache for
145
149
*/
146
150
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 ();
153
155
}
154
156
}
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 ();
161
167
}
162
168
}
163
169
}
@@ -170,35 +176,25 @@ public static void clearClassLoader(ClassLoader classLoader) {
170
176
*/
171
177
@ SuppressWarnings ("unchecked" )
172
178
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 ;
177
182
}
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 ;
181
186
}
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 );
184
192
}
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" );
201
196
}
197
+ softClassCache .put (beanClass , results );
202
198
}
203
199
return results ;
204
200
}
@@ -211,13 +207,7 @@ static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansExcep
211
207
* @see #acceptClassLoader
212
208
*/
213
209
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 ) {
221
211
if (isUnderneathClassLoader (classLoader , acceptedLoader )) {
222
212
return true ;
223
213
}
0 commit comments