Merge pull request #166 from philwebb/SPR-9851
# By Phillip Webb * SPR-9851: Prevent memory leaks with @Configuration beans
This commit is contained in:
commit
27c83710b0
|
|
@ -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,11 +52,28 @@ 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 static final CallbackFilter CALLBACK_FILTER = new CallbackFilter() {
|
||||
|
||||
private final CallbackFilter 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.
|
||||
if (BeanAnnotationHelper.isBeanAnnotated(candidateMethod)) {
|
||||
return 0;
|
||||
}
|
||||
if (DisposableBeanMethodInterceptor.isDestroyMethod(candidateMethod)) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
private static final Callback DISPOSABLE_BEAN_METHOD_INTERCEPTOR = new DisposableBeanMethodInterceptor();
|
||||
|
||||
|
||||
private final Callback[] callbackInstances;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -66,28 +81,11 @@ class ConfigurationClassEnhancer {
|
|||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
if (DisposableBeanMethodInterceptor.isDestroyMethod(candidateMethod)) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +160,7 @@ class ConfigurationClassEnhancer {
|
|||
* @see BeanMethodInterceptor#enhanceFactoryBean(Class, String)
|
||||
*/
|
||||
private static class GetObjectMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
|
||||
private final ConfigurableBeanFactory beanFactory;
|
||||
private final String beanName;
|
||||
|
||||
|
|
@ -178,7 +172,7 @@ class ConfigurationClassEnhancer {
|
|||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
return beanFactory.getBean(beanName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -246,7 +251,7 @@ class ConfigurationClassEnhancer {
|
|||
|
||||
// to handle the case of an inter-bean method reference, we must explicitly check the
|
||||
// container for already cached instances
|
||||
|
||||
|
||||
// first, check to see if the requested bean is a FactoryBean. If so, create a subclass
|
||||
// proxy that intercepts calls to getObject() and returns any cached bean instance.
|
||||
// this ensures that the semantics of calling a FactoryBean from within @Bean methods
|
||||
|
|
@ -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.setCallbackTypes(callbackTypes.toArray(new Class[callbackTypes.size()]));
|
||||
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(CALLBACK_TYPES);
|
||||
Class<?> fbSubclass = enhancer.createClass();
|
||||
Enhancer.registerCallbacks(fbSubclass, callbackInstances.toArray(new Callback[callbackInstances.size()]));
|
||||
Enhancer.registerCallbacks(fbSubclass, callbackInstances);
|
||||
return fbSubclass.newInstance();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue