1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.context.annotation;
18
19 import java.lang.reflect.Field;
20 import java.lang.reflect.Method;
21 import java.util.Arrays;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25
26 import org.springframework.aop.scope.ScopedProxyFactoryBean;
27 import org.springframework.asm.Type;
28 import org.springframework.beans.factory.BeanFactory;
29 import org.springframework.beans.factory.BeanFactoryAware;
30 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
31 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
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;
36 import org.springframework.cglib.core.SpringNamingPolicy;
37 import org.springframework.cglib.proxy.Callback;
38 import org.springframework.cglib.proxy.CallbackFilter;
39 import org.springframework.cglib.proxy.Enhancer;
40 import org.springframework.cglib.proxy.MethodInterceptor;
41 import org.springframework.cglib.proxy.MethodProxy;
42 import org.springframework.cglib.proxy.NoOp;
43 import org.springframework.cglib.transform.ClassEmitterTransformer;
44 import org.springframework.cglib.transform.TransformingClassGenerator;
45 import org.springframework.core.annotation.AnnotationUtils;
46 import org.springframework.util.Assert;
47 import org.springframework.util.ObjectUtils;
48 import org.springframework.util.ReflectionUtils;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 class ConfigurationClassEnhancer {
66
67
68 private static final Callback[] CALLBACKS = new Callback[] {
69 new BeanMethodInterceptor(),
70 new BeanFactoryAwareMethodInterceptor(),
71 NoOp.INSTANCE
72 };
73
74 private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
75
76 private static final DefaultGeneratorStrategy GENERATOR_STRATEGY = new BeanFactoryAwareGeneratorStrategy();
77
78 private static final String BEAN_FACTORY_FIELD = "$$beanFactory";
79
80 private static final Log logger = LogFactory.getLog(ConfigurationClassEnhancer.class);
81
82
83
84
85
86
87
88 public Class<?> enhance(Class<?> configClass) {
89 if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
90 if (logger.isDebugEnabled()) {
91 logger.debug(String.format("Ignoring request to enhance %s as it has " +
92 "already been enhanced. This usually indicates that more than one " +
93 "ConfigurationClassPostProcessor has been registered (e.g. via " +
94 "<context:annotation-config>). This is harmless, but you may " +
95 "want check your configuration and remove one CCPP if possible",
96 configClass.getName()));
97 }
98 return configClass;
99 }
100 Class<?> enhancedClass = createClass(newEnhancer(configClass));
101 if (logger.isDebugEnabled()) {
102 logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
103 configClass.getName(), enhancedClass.getName()));
104 }
105 return enhancedClass;
106 }
107
108
109
110
111 private Enhancer newEnhancer(Class<?> superclass) {
112 Enhancer enhancer = new Enhancer();
113 enhancer.setSuperclass(superclass);
114 enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
115 enhancer.setUseFactory(false);
116 enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
117 enhancer.setStrategy(GENERATOR_STRATEGY);
118 enhancer.setCallbackFilter(CALLBACK_FILTER);
119 enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
120 return enhancer;
121 }
122
123
124
125
126
127 private Class<?> createClass(Enhancer enhancer) {
128 Class<?> subclass = enhancer.createClass();
129
130
131 Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
132 return subclass;
133 }
134
135
136
137
138
139
140
141
142
143
144
145
146
147 public interface EnhancedConfiguration extends BeanFactoryAware {
148 }
149
150
151
152
153
154
155 private static interface ConditionalCallback extends Callback {
156
157 boolean isMatch(Method candidateMethod);
158 }
159
160
161
162
163
164
165 private static class ConditionalCallbackFilter implements CallbackFilter {
166
167 private final Callback[] callbacks;
168
169 private final Class<?>[] callbackTypes;
170
171 public ConditionalCallbackFilter(Callback[] callbacks) {
172 this.callbacks = callbacks;
173 this.callbackTypes = new Class<?>[callbacks.length];
174 for (int i = 0; i < callbacks.length; i++) {
175 this.callbackTypes[i] = callbacks[i].getClass();
176 }
177 }
178
179 @Override
180 public int accept(Method method) {
181 for (int i = 0; i < this.callbacks.length; i++) {
182 if (!(this.callbacks[i] instanceof ConditionalCallback) ||
183 ((ConditionalCallback) this.callbacks[i]).isMatch(method)) {
184 return i;
185 }
186 }
187 throw new IllegalStateException("No callback available for method " + method.getName());
188 }
189
190 public Class<?>[] getCallbackTypes() {
191 return this.callbackTypes;
192 }
193 }
194
195
196
197
198
199 private static class BeanFactoryAwareGeneratorStrategy extends DefaultGeneratorStrategy {
200
201 @Override
202 protected ClassGenerator transform(ClassGenerator cg) throws Exception {
203 ClassEmitterTransformer transformer = new ClassEmitterTransformer() {
204 @Override
205 public void end_class() {
206 declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null);
207 super.end_class();
208 }
209 };
210 return new TransformingClassGenerator(cg, transformer);
211 }
212 }
213
214
215
216
217
218
219
220 private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor, ConditionalCallback {
221
222 @Override
223 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
224 Field field = obj.getClass().getDeclaredField(BEAN_FACTORY_FIELD);
225 Assert.state(field != null, "Unable to find generated BeanFactory field");
226 field.set(obj, args[0]);
227
228
229
230 if (BeanFactoryAware.class.isAssignableFrom(obj.getClass().getSuperclass())) {
231 return proxy.invokeSuper(obj, args);
232 }
233 return null;
234 }
235
236 @Override
237 public boolean isMatch(Method candidateMethod) {
238 return (candidateMethod.getName().equals("setBeanFactory") &&
239 candidateMethod.getParameterTypes().length == 1 &&
240 candidateMethod.getParameterTypes()[0].equals(BeanFactory.class) &&
241 BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
242 }
243 }
244
245
246
247
248
249
250
251
252 private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {
253
254
255
256
257
258
259
260 @Override
261 public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
262 MethodProxy cglibMethodProxy) throws Throwable {
263
264 ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
265 String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
266
267
268 Scope scope = AnnotationUtils.findAnnotation(beanMethod, Scope.class);
269 if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
270 String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
271 if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
272 beanName = scopedBeanName;
273 }
274 }
275
276
277
278
279
280
281
282
283 if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
284 factoryContainsBean(beanFactory, beanName)) {
285 Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
286 if (factoryBean instanceof ScopedProxyFactoryBean) {
287
288
289 }
290 else {
291
292 return enhanceFactoryBean(factoryBean.getClass(), beanFactory, beanName);
293 }
294 }
295
296 if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
297
298
299
300 if (BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
301 logger.warn(String.format("@Bean method %s.%s is non-static and returns an object " +
302 "assignable to Spring's BeanFactoryPostProcessor interface. This will " +
303 "result in a failure to process annotations such as @Autowired, " +
304 "@Resource and @PostConstruct within the method's declaring " +
305 "@Configuration class. Add the 'static' modifier to this method to avoid " +
306 "these container lifecycle issues; see @Bean javadoc for complete details",
307 beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
308 }
309 return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
310 }
311 else {
312
313
314
315
316 boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);
317 try {
318 if (alreadyInCreation) {
319 beanFactory.setCurrentlyInCreation(beanName, false);
320 }
321 return (!ObjectUtils.isEmpty(beanMethodArgs) ?
322 beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName));
323 }
324 finally {
325 if (alreadyInCreation) {
326 beanFactory.setCurrentlyInCreation(beanName, true);
327 }
328 }
329 }
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 private boolean factoryContainsBean(ConfigurableBeanFactory beanFactory, String beanName) {
346 return (beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName));
347 }
348
349
350
351
352
353
354
355 private boolean isCurrentlyInvokedFactoryMethod(Method method) {
356 Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
357 return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
358 Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
359 }
360
361
362
363
364
365
366
367
368 private Object enhanceFactoryBean(Class<?> fbClass, final ConfigurableBeanFactory beanFactory,
369 final String beanName) throws InstantiationException, IllegalAccessException {
370
371 Enhancer enhancer = new Enhancer();
372 enhancer.setSuperclass(fbClass);
373 enhancer.setUseFactory(false);
374 enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
375 enhancer.setCallback(new MethodInterceptor() {
376 @Override
377 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
378 if (method.getName().equals("getObject") && args.length == 0) {
379 return beanFactory.getBean(beanName);
380 }
381 return proxy.invokeSuper(obj, args);
382 }
383 });
384 return enhancer.create();
385 }
386
387 private ConfigurableBeanFactory getBeanFactory(Object enhancedConfigInstance) {
388 Field field = ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD);
389 Assert.state(field != null, "Unable to find generated bean factory field");
390 Object beanFactory = ReflectionUtils.getField(field, enhancedConfigInstance);
391 Assert.state(beanFactory != null, "BeanFactory has not been injected into @Configuration class");
392 Assert.state(beanFactory instanceof ConfigurableBeanFactory,
393 "Injected BeanFactory is not a ConfigurableBeanFactory");
394 return (ConfigurableBeanFactory) beanFactory;
395 }
396
397 @Override
398 public boolean isMatch(Method candidateMethod) {
399 return BeanAnnotationHelper.isBeanAnnotated(candidateMethod);
400 }
401 }
402
403 }