Merge pull request #166 from philwebb/SPR-9851

# By Phillip Webb
* SPR-9851:
  Prevent memory leaks with @Configuration beans
master
Chris Beams 12 years ago
commit 27c83710b0
  1. 89
      spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java

@ -17,8 +17,6 @@
package org.springframework.context.annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -54,31 +52,14 @@ class ConfigurationClassEnhancer {
private static final Log logger = LogFactory.getLog(ConfigurationClassEnhancer.class);
private final List<Callback> callbackInstances = new ArrayList<Callback>();
private static final Class<?>[] CALLBACK_TYPES = { BeanMethodInterceptor.class,
DisposableBeanMethodInterceptor.class, NoOp.class };
private final List<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
private final CallbackFilter callbackFilter;
/**
* Creates a new {@link ConfigurationClassEnhancer} instance.
*/
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.callbackInstances.add(new BeanMethodInterceptor(beanFactory));
this.callbackInstances.add(new DisposableBeanMethodInterceptor());
this.callbackInstances.add(NoOp.INSTANCE);
for (Callback callback : this.callbackInstances) {
this.callbackTypes.add(callback.getClass());
}
private static final CallbackFilter CALLBACK_FILTER = new CallbackFilter() {
public int accept(Method candidateMethod) {
// Set up the callback filter to return the index of the BeanMethodInterceptor when
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
callbackFilter = new CallbackFilter() {
public int accept(Method candidateMethod) {
if (BeanAnnotationHelper.isBeanAnnotated(candidateMethod)) {
return 0;
}
@ -88,6 +69,23 @@ class ConfigurationClassEnhancer {
return 2;
}
};
private static final Callback DISPOSABLE_BEAN_METHOD_INTERCEPTOR = new DisposableBeanMethodInterceptor();
private final Callback[] callbackInstances;
/**
* Creates a new {@link ConfigurationClassEnhancer} instance.
*/
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
// Callback instances must be ordered in the same way as CALLBACK_TYPES and CALLBACK_FILTER
this.callbackInstances = new Callback[] {
new BeanMethodInterceptor(beanFactory),
DISPOSABLE_BEAN_METHOD_INTERCEPTOR,
NoOp.INSTANCE };
}
/**
@ -135,15 +133,11 @@ class ConfigurationClassEnhancer {
*/
private Enhancer newEnhancer(Class<?> superclass) {
Enhancer enhancer = new Enhancer();
// Because callbackFilter and callbackTypes are dynamically populated
// there's no opportunity for caching. This does not appear to be causing
// any performance problem.
enhancer.setUseCache(false);
enhancer.setSuperclass(superclass);
enhancer.setInterfaces(new Class[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setCallbackFilter(this.callbackFilter);
enhancer.setCallbackTypes(this.callbackTypes.toArray(new Class[this.callbackTypes.size()]));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_TYPES);
return enhancer;
}
@ -154,7 +148,7 @@ class ConfigurationClassEnhancer {
private Class<?> createClass(Enhancer enhancer) {
Class<?> subclass = enhancer.createClass();
// registering callbacks statically (as opposed to threadlocal) is critical for usage in an OSGi env (SPR-5932)
Enhancer.registerStaticCallbacks(subclass, this.callbackInstances.toArray(new Callback[this.callbackInstances.size()]));
Enhancer.registerStaticCallbacks(subclass, this.callbackInstances);
return subclass;
}
@ -216,8 +210,19 @@ class ConfigurationClassEnhancer {
*/
private static class BeanMethodInterceptor implements MethodInterceptor {
private static final Class<?>[] CALLBACK_TYPES = {
GetObjectMethodInterceptor.class, NoOp.class };
private static final CallbackFilter CALLBACK_FITLER = new CallbackFilter() {
public int accept(Method method) {
return method.getName().equals("getObject") ? 0 : 1;
}
};
private final ConfigurableBeanFactory beanFactory;
public BeanMethodInterceptor(ConfigurableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@ -328,26 +333,18 @@ class ConfigurationClassEnhancer {
*/
private Object enhanceFactoryBean(Class<?> fbClass, String beanName) throws InstantiationException, IllegalAccessException {
Enhancer enhancer = new Enhancer();
enhancer.setUseCache(false);
enhancer.setSuperclass(fbClass);
enhancer.setUseFactory(false);
enhancer.setCallbackFilter(new CallbackFilter() {
public int accept(Method method) {
return method.getName().equals("getObject") ? 0 : 1;
}
});
List<Callback> callbackInstances = new ArrayList<Callback>();
callbackInstances.add(new GetObjectMethodInterceptor(this.beanFactory, beanName));
callbackInstances.add(NoOp.INSTANCE);
List<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
for (Callback callback : callbackInstances) {
callbackTypes.add(callback.getClass());
}
enhancer.setCallbackFilter(CALLBACK_FITLER);
// Callback instances must be ordered in the same way as CALLBACK_TYPES and CALLBACK_FILTER
Callback[] callbackInstances = new Callback[] {
new GetObjectMethodInterceptor(this.beanFactory, beanName),
NoOp.INSTANCE
};
enhancer.setCallbackTypes(callbackTypes.toArray(new Class[callbackTypes.size()]));
enhancer.setCallbackTypes(CALLBACK_TYPES);
Class<?> fbSubclass = enhancer.createClass();
Enhancer.registerCallbacks(fbSubclass, callbackInstances.toArray(new Callback[callbackInstances.size()]));
Enhancer.registerCallbacks(fbSubclass, callbackInstances);
return fbSubclass.newInstance();
}

Loading…
Cancel
Save