16
16
17
17
package org .springframework .context .annotation ;
18
18
19
+ import java .lang .reflect .Field ;
19
20
import java .lang .reflect .Method ;
20
21
import java .util .Arrays ;
21
22
22
23
import org .apache .commons .logging .Log ;
23
24
import org .apache .commons .logging .LogFactory ;
24
-
25
25
import org .springframework .aop .scope .ScopedProxyFactoryBean ;
26
+ import org .springframework .asm .Type ;
26
27
import org .springframework .beans .factory .BeanFactory ;
28
+ import org .springframework .beans .factory .BeanFactoryAware ;
27
29
import org .springframework .beans .factory .DisposableBean ;
28
- import org .springframework .beans .factory .FactoryBean ;
29
30
import org .springframework .beans .factory .config .BeanFactoryPostProcessor ;
30
31
import org .springframework .beans .factory .config .ConfigurableBeanFactory ;
31
32
import org .springframework .beans .factory .support .SimpleInstantiationStrategy ;
33
+ import org .springframework .cglib .core .ClassGenerator ;
34
+ import org .springframework .cglib .core .Constants ;
35
+ import org .springframework .cglib .core .DefaultGeneratorStrategy ;
32
36
import org .springframework .cglib .proxy .Callback ;
33
37
import org .springframework .cglib .proxy .CallbackFilter ;
34
38
import org .springframework .cglib .proxy .Enhancer ;
35
39
import org .springframework .cglib .proxy .MethodInterceptor ;
36
40
import org .springframework .cglib .proxy .MethodProxy ;
37
41
import org .springframework .cglib .proxy .NoOp ;
42
+ import org .springframework .cglib .transform .ClassEmitterTransformer ;
43
+ import org .springframework .cglib .transform .TransformingClassGenerator ;
38
44
import org .springframework .core .annotation .AnnotationUtils ;
39
45
import org .springframework .util .Assert ;
46
+ import org .springframework .util .ReflectionUtils ;
40
47
41
48
/**
42
49
* Enhances {@link Configuration} classes by generating a CGLIB subclass capable of
@@ -52,25 +59,18 @@ class ConfigurationClassEnhancer {
52
59
53
60
private static final Log logger = LogFactory .getLog (ConfigurationClassEnhancer .class );
54
61
55
- private static final CallbackFilter CALLBACK_FILTER = new ConfigurationClassCallbackFilter ();
56
-
57
- private static final Callback DISPOSABLE_BEAN_METHOD_INTERCEPTOR = new DisposableBeanMethodInterceptor ();
58
-
59
- private static final Class <?>[] CALLBACK_TYPES =
60
- { BeanMethodInterceptor . class , DisposableBeanMethodInterceptor . class , NoOp . class };
62
+ private static final Callback [] CALLBACKS = new Callback [] {
63
+ new BeanMethodInterceptor (),
64
+ new DisposableBeanMethodInterceptor (),
65
+ new BeanFactoryAwareMethodInterceptor (),
66
+ NoOp . INSTANCE
67
+ };
61
68
62
- private final Callback [] callbackInstances ;
69
+ private static final ConditionalCallbackFilter CALLBACK_FILTER =
70
+ new ConditionalCallbackFilter (CALLBACKS );
63
71
64
72
65
- /**
66
- * Creates a new {@link ConfigurationClassEnhancer} instance.
67
- */
68
- public ConfigurationClassEnhancer (ConfigurableBeanFactory beanFactory ) {
69
- Assert .notNull (beanFactory , "BeanFactory must not be null" );
70
- // Callback instances must be ordered in the same way as CALLBACK_TYPES and CALLBACK_FILTER
71
- this .callbackInstances = new Callback []
72
- {new BeanMethodInterceptor (beanFactory ), DISPOSABLE_BEAN_METHOD_INTERCEPTOR , NoOp .INSTANCE };
73
- }
73
+ private static final String BEAN_FACTORY_FIELD = "$$beanFactory" ;
74
74
75
75
/**
76
76
* Loads the specified class and generates a CGLIB subclass of it equipped with
@@ -106,7 +106,21 @@ private Enhancer newEnhancer(Class<?> superclass) {
106
106
enhancer .setInterfaces (new Class [] {EnhancedConfiguration .class });
107
107
enhancer .setUseFactory (false );
108
108
enhancer .setCallbackFilter (CALLBACK_FILTER );
109
- enhancer .setCallbackTypes (CALLBACK_TYPES );
109
+ enhancer .setCallbackTypes (CALLBACK_FILTER .getCallbackTypes ());
110
+ enhancer .setStrategy (new DefaultGeneratorStrategy () {
111
+ @ Override
112
+ protected ClassGenerator transform (ClassGenerator cg ) throws Exception {
113
+ ClassEmitterTransformer transformer = new ClassEmitterTransformer () {
114
+ @ Override
115
+ public void end_class () {
116
+ declare_field (Constants .ACC_PUBLIC , BEAN_FACTORY_FIELD ,
117
+ Type .getType (BeanFactory .class ), null );
118
+ super .end_class ();
119
+ }
120
+ };
121
+ return new TransformingClassGenerator (cg , transformer );
122
+ }
123
+ });
110
124
return enhancer ;
111
125
}
112
126
@@ -117,7 +131,7 @@ private Enhancer newEnhancer(Class<?> superclass) {
117
131
private Class <?> createClass (Enhancer enhancer ) {
118
132
Class <?> subclass = enhancer .createClass ();
119
133
// registering callbacks statically (as opposed to threadlocal) is critical for usage in an OSGi env (SPR-5932)
120
- Enhancer .registerStaticCallbacks (subclass , this . callbackInstances );
134
+ Enhancer .registerStaticCallbacks (subclass , CALLBACKS );
121
135
return subclass ;
122
136
}
123
137
@@ -128,67 +142,75 @@ private Class<?> createClass(Enhancer enhancer) {
128
142
* Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance(Class)}
129
143
* through checking to see if candidate classes are already assignable to it, e.g.
130
144
* have already been enhanced.
131
- * <p>Also extends {@link DisposableBean}, as all enhanced
132
- * {@code @Configuration} classes must de-register static CGLIB callbacks on
133
- * destruction, which is handled by the (private) {@code DisposableBeanMethodInterceptor}.
145
+ * <p>Also extends {@link DisposableBean} and {@link BeanFactoryAware}, as all
146
+ * enhanced {@code @Configuration} classes require access to the {@link BeanFactory}
147
+ * that created them and must de-register static CGLIB callbacks on destruction,
148
+ * which is handled by the (private) {@code DisposableBeanMethodInterceptor}.
134
149
* <p>Note that this interface is intended for framework-internal use only, however
135
150
* must remain public in order to allow access to subclasses generated from other
136
151
* packages (i.e. user code).
137
152
*/
138
- public interface EnhancedConfiguration extends DisposableBean {
153
+ public interface EnhancedConfiguration extends DisposableBean , BeanFactoryAware {
139
154
}
140
155
141
156
142
157
/**
143
- * CGLIB CallbackFilter implementation that points to BeanMethodInterceptor and
144
- * DisposableBeanMethodInterceptor.
158
+ * Conditional {@link Callback}.
159
+ * @see ConditionalCallbackFilter
145
160
*/
146
- private static class ConfigurationClassCallbackFilter implements CallbackFilter {
147
-
148
- public int accept (Method candidateMethod ) {
149
- // Set up the callback filter to return the index of the BeanMethodInterceptor when
150
- // handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
151
- if (BeanAnnotationHelper .isBeanAnnotated (candidateMethod )) {
152
- return 0 ;
153
- }
154
- if (DisposableBeanMethodInterceptor .isDestroyMethod (candidateMethod )) {
155
- return 1 ;
156
- }
157
- return 2 ;
158
- }
161
+ private static interface ConditionalCallback extends Callback {
162
+ boolean isMatch (Method candidateMethod );
159
163
}
160
164
161
-
162
165
/**
163
- * Intercepts calls to {@link FactoryBean#getObject()}, delegating to calling
164
- * {@link BeanFactory#getBean(String)} in order to respect caching / scoping.
165
- * @see BeanMethodInterceptor#intercept(Object, Method, Object[], MethodProxy)
166
- * @see BeanMethodInterceptor#enhanceFactoryBean(Class, String)
166
+ * A {@link CallbackFilter} that works by interrogating {@link Callback}s in the order
167
+ * that they are defined via {@link ConditionalCallback}.
167
168
*/
168
- private static class GetObjectMethodInterceptor implements MethodInterceptor {
169
+ private static class ConditionalCallbackFilter implements CallbackFilter {
169
170
170
- private final ConfigurableBeanFactory beanFactory ;
171
+ private final Callback [] callbacks ;
171
172
172
- private final String beanName ;
173
+ private final Class <?>[] callbackTypes ;
173
174
174
- public GetObjectMethodInterceptor (ConfigurableBeanFactory beanFactory , String beanName ) {
175
- this .beanFactory = beanFactory ;
176
- this .beanName = beanName ;
175
+ public ConditionalCallbackFilter (Callback [] callbacks ) {
176
+ this .callbacks = callbacks ;
177
+ this .callbackTypes = new Class <?>[callbacks .length ];
178
+ for (int i = 0 ; i < callbacks .length ; i ++) {
179
+ callbackTypes [i ] = callbacks [i ].getClass ();
180
+ }
177
181
}
178
182
179
- public Object intercept (Object obj , Method method , Object [] args , MethodProxy proxy ) throws Throwable {
180
- return this .beanFactory .getBean (this .beanName );
183
+ @ Override
184
+ public int accept (Method method ) {
185
+ for (int i = 0 ; i < callbacks .length ; i ++) {
186
+ if (!(callbacks [i ] instanceof ConditionalCallback ) ||
187
+ ((ConditionalCallback ) callbacks [i ]).isMatch (method )) {
188
+ return i ;
189
+ }
190
+ }
191
+ throw new IllegalStateException ("No callback available for method "
192
+ + method .getName ());
181
193
}
182
- }
183
194
195
+ public Class <?>[] getCallbackTypes () {
196
+ return callbackTypes ;
197
+ }
198
+ }
184
199
185
200
/**
186
201
* Intercepts the invocation of any {@link DisposableBean#destroy()} on @Configuration
187
202
* class instances for the purpose of de-registering CGLIB callbacks. This helps avoid
188
203
* garbage collection issues. See SPR-7901.
189
204
* @see EnhancedConfiguration
190
205
*/
191
- private static class DisposableBeanMethodInterceptor implements MethodInterceptor {
206
+ private static class DisposableBeanMethodInterceptor implements MethodInterceptor ,
207
+ ConditionalCallback {
208
+
209
+ public boolean isMatch (Method candidateMethod ) {
210
+ return candidateMethod .getName ().equals ("destroy" )
211
+ && candidateMethod .getParameterTypes ().length == 0
212
+ && DisposableBean .class .isAssignableFrom (candidateMethod .getDeclaringClass ());
213
+ }
192
214
193
215
public Object intercept (Object obj , Method method , Object [] args , MethodProxy proxy ) throws Throwable {
194
216
Enhancer .registerStaticCallbacks (obj .getClass (), null );
@@ -200,10 +222,38 @@ public Object intercept(Object obj, Method method, Object[] args, MethodProxy pr
200
222
return null ;
201
223
}
202
224
203
- public static boolean isDestroyMethod (Method candidateMethod ) {
204
- return candidateMethod .getName ().equals ("destroy" ) &&
205
- candidateMethod .getParameterTypes ().length == 0 &&
206
- DisposableBean .class .isAssignableFrom (candidateMethod .getDeclaringClass ());
225
+ }
226
+
227
+
228
+ /**
229
+ * Intercepts the invocation of any
230
+ * {@link BeanFactoryAware#setBeanFactory(BeanFactory)} on {@code @Configuration}
231
+ * class instances for the purpose of recording the {@link BeanFactory}.
232
+ *
233
+ * @see EnhancedConfiguration
234
+ */
235
+ private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor ,
236
+ ConditionalCallback {
237
+
238
+ public boolean isMatch (Method candidateMethod ) {
239
+ return candidateMethod .getName ().equals ("setBeanFactory" )
240
+ && candidateMethod .getParameterTypes ().length == 1
241
+ && candidateMethod .getParameterTypes ()[0 ].equals (BeanFactory .class )
242
+ && BeanFactoryAware .class .isAssignableFrom (candidateMethod .getDeclaringClass ());
243
+ }
244
+
245
+ public Object intercept (Object obj , Method method , Object [] args ,
246
+ MethodProxy proxy ) throws Throwable {
247
+ Field field = obj .getClass ().getDeclaredField (BEAN_FACTORY_FIELD );
248
+ Assert .state (field != null , "Unable to find generated bean factory field" );
249
+ field .set (obj , args [0 ]);
250
+
251
+ // does the actual (non-CGLIB) superclass actually implement BeanFactoryAware?
252
+ // if so, call its setBeanFactory() method. If not, just exit.
253
+ if (BeanFactoryAware .class .isAssignableFrom (obj .getClass ().getSuperclass ())) {
254
+ return proxy .invokeSuper (obj , args );
255
+ }
256
+ return null ;
207
257
}
208
258
}
209
259
@@ -214,20 +264,10 @@ public static boolean isDestroyMethod(Method candidateMethod) {
214
264
* @see Bean
215
265
* @see ConfigurationClassEnhancer
216
266
*/
217
- private static class BeanMethodInterceptor implements MethodInterceptor {
218
-
219
- private static final Class <?>[] CALLBACK_TYPES = {GetObjectMethodInterceptor .class , NoOp .class };
220
-
221
- private static final CallbackFilter CALLBACK_FILTER = new CallbackFilter () {
222
- public int accept (Method method ) {
223
- return (method .getName ().equals ("getObject" ) ? 0 : 1 );
224
- }
225
- };
226
-
227
- private final ConfigurableBeanFactory beanFactory ;
267
+ private static class BeanMethodInterceptor implements MethodInterceptor , ConditionalCallback {
228
268
229
- public BeanMethodInterceptor ( ConfigurableBeanFactory beanFactory ) {
230
- this . beanFactory = beanFactory ;
269
+ public boolean isMatch ( Method candidateMethod ) {
270
+ return BeanAnnotationHelper . isBeanAnnotated ( candidateMethod ) ;
231
271
}
232
272
233
273
/**
@@ -240,13 +280,14 @@ public BeanMethodInterceptor(ConfigurableBeanFactory beanFactory) {
240
280
public Object intercept (Object enhancedConfigInstance , Method beanMethod , Object [] beanMethodArgs ,
241
281
MethodProxy cglibMethodProxy ) throws Throwable {
242
282
283
+ ConfigurableBeanFactory beanFactory = getBeanFactory (enhancedConfigInstance );
243
284
String beanName = BeanAnnotationHelper .determineBeanNameFor (beanMethod );
244
285
245
286
// determine whether this bean is a scoped-proxy
246
287
Scope scope = AnnotationUtils .findAnnotation (beanMethod , Scope .class );
247
288
if (scope != null && scope .proxyMode () != ScopedProxyMode .NO ) {
248
289
String scopedBeanName = ScopedProxyCreator .getTargetBeanName (beanName );
249
- if (this . beanFactory .isCurrentlyInCreation (scopedBeanName )) {
290
+ if (beanFactory .isCurrentlyInCreation (scopedBeanName )) {
250
291
beanName = scopedBeanName ;
251
292
}
252
293
}
@@ -258,19 +299,20 @@ public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object
258
299
// proxy that intercepts calls to getObject() and returns any cached bean instance.
259
300
// this ensures that the semantics of calling a FactoryBean from within @Bean methods
260
301
// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
261
- if (factoryContainsBean (BeanFactory .FACTORY_BEAN_PREFIX + beanName ) && factoryContainsBean (beanName )) {
262
- Object factoryBean = this .beanFactory .getBean (BeanFactory .FACTORY_BEAN_PREFIX + beanName );
302
+ if (factoryContainsBean (beanFactory , BeanFactory .FACTORY_BEAN_PREFIX + beanName ) &&
303
+ factoryContainsBean (beanFactory , beanName )) {
304
+ Object factoryBean = beanFactory .getBean (BeanFactory .FACTORY_BEAN_PREFIX + beanName );
263
305
if (factoryBean instanceof ScopedProxyFactoryBean ) {
264
306
// pass through - scoped proxy factory beans are a special case and should not
265
307
// be further proxied
266
308
}
267
309
else {
268
310
// it is a candidate FactoryBean - go ahead with enhancement
269
- return enhanceFactoryBean (factoryBean .getClass (), beanName );
311
+ return enhanceFactoryBean (factoryBean .getClass (), beanFactory , beanName );
270
312
}
271
313
}
272
314
273
- if (isCurrentlyInvokedFactoryMethod (beanMethod ) && !this . beanFactory .containsSingleton (beanName )) {
315
+ if (isCurrentlyInvokedFactoryMethod (beanMethod ) && !beanFactory .containsSingleton (beanName )) {
274
316
// the factory is calling the bean method in order to instantiate and register the bean
275
317
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
276
318
// create the bean instance.
@@ -290,20 +332,19 @@ public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object
290
332
// call to the bean method, direct or indirect. The bean may have already been
291
333
// marked as 'in creation' in certain autowiring scenarios; if so, temporarily
292
334
// set the in-creation status to false in order to avoid an exception.
293
- boolean alreadyInCreation = this . beanFactory .isCurrentlyInCreation (beanName );
335
+ boolean alreadyInCreation = beanFactory .isCurrentlyInCreation (beanName );
294
336
try {
295
337
if (alreadyInCreation ) {
296
- this . beanFactory .setCurrentlyInCreation (beanName , false );
338
+ beanFactory .setCurrentlyInCreation (beanName , false );
297
339
}
298
- return this . beanFactory .getBean (beanName );
340
+ return beanFactory .getBean (beanName );
299
341
}
300
342
finally {
301
343
if (alreadyInCreation ) {
302
- this . beanFactory .setCurrentlyInCreation (beanName , true );
344
+ beanFactory .setCurrentlyInCreation (beanName , true );
303
345
}
304
346
}
305
347
}
306
-
307
348
}
308
349
309
350
/**
@@ -319,8 +360,8 @@ public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object
319
360
* @param beanName name of bean to check for
320
361
* @return whether <var>beanName</var> already exists in the factory
321
362
*/
322
- private boolean factoryContainsBean (String beanName ) {
323
- return (this . beanFactory .containsBean (beanName ) && !this . beanFactory .isCurrentlyInCreation (beanName ));
363
+ private boolean factoryContainsBean (ConfigurableBeanFactory beanFactory , String beanName ) {
364
+ return (beanFactory .containsBean (beanName ) && !beanFactory .isCurrentlyInCreation (beanName ));
324
365
}
325
366
326
367
/**
@@ -342,20 +383,29 @@ private boolean isCurrentlyInvokedFactoryMethod(Method method) {
342
383
* instance directly. If a FactoryBean instance is fetched through the container via &-dereferencing,
343
384
* it will not be proxied. This too is aligned with the way XML configuration works.
344
385
*/
345
- private Object enhanceFactoryBean (Class <?> fbClass , String beanName ) throws InstantiationException , IllegalAccessException {
386
+ private Object enhanceFactoryBean (Class <?> fbClass , final ConfigurableBeanFactory beanFactory ,
387
+ final String beanName ) throws InstantiationException , IllegalAccessException {
346
388
Enhancer enhancer = new Enhancer ();
347
389
enhancer .setSuperclass (fbClass );
348
390
enhancer .setUseFactory (false );
349
- enhancer .setCallbackFilter (CALLBACK_FILTER );
350
- // Callback instances must be ordered in the same way as CALLBACK_TYPES and CALLBACK_FILTER
351
- Callback [] callbackInstances = new Callback [] {
352
- new GetObjectMethodInterceptor (this .beanFactory , beanName ),
353
- NoOp .INSTANCE
354
- };
355
- enhancer .setCallbackTypes (CALLBACK_TYPES );
356
- Class <?> fbSubclass = enhancer .createClass ();
357
- Enhancer .registerCallbacks (fbSubclass , callbackInstances );
358
- return fbSubclass .newInstance ();
391
+ enhancer .setCallback (new MethodInterceptor () {
392
+ public Object intercept (Object obj , Method method , Object [] args ,
393
+ MethodProxy proxy ) throws Throwable {
394
+ if (method .getName ().equals ("getObject" ) && args .length == 0 ) {
395
+ return beanFactory .getBean (beanName );
396
+ }
397
+ return proxy .invokeSuper (obj , args );
398
+ }
399
+ });
400
+ return enhancer .create ();
401
+ }
402
+
403
+ private ConfigurableBeanFactory getBeanFactory (Object enhancedConfigInstance ) {
404
+ Field field = ReflectionUtils .findField (enhancedConfigInstance .getClass (), BEAN_FACTORY_FIELD );
405
+ Assert .state (field != null , "Unable to find generated bean factory field" );
406
+ Object beanFactory = ReflectionUtils .getField (field , enhancedConfigInstance );
407
+ Assert .isInstanceOf (ConfigurableBeanFactory .class , beanFactory );
408
+ return (ConfigurableBeanFactory ) beanFactory ;
359
409
}
360
410
}
361
411
0 commit comments