Instance.destroy() actually destroys the underlying bean for @ApplicationScoped instead of the proxy
joshuak Mar 3, 2015 4:04 PMTo dynamically inject instances of an interface, we are using code similar to the following:
@ApplicationScoped public class SomeCDIBean { @Inject @Any private Instance<SomeInterface> interfaceInstance; public void someMethod() { SomeInterface instance = interfaceInstance.select(new Qualifier("someValue")).get(); instance.doSomething(); interfaceInstance.destroy(instance); } }
Implementations of SomeInterface can be either @Dependent or @ApplicationScoped. In order to prevent a memory leak destroy() is called on the dynamically injected instances of SomeInterface. This works as expected for @Dependent beans, however, with @ApplicationScoped beans, this destroys the actual bean itself instead of just the proxy, causing future calls against that specific instance to be in an incorrect state.
After some research I found the following in the org.jboss.weld.bean.builtin.InstanceImpl.destroy() method:
// check if this is a proxy of a normal-scoped bean if (instance instanceof ProxyObject) { ProxyObject proxy = (ProxyObject) instance; if (proxy.getHandler() instanceof ProxyMethodHandler) { ProxyMethodHandler handler = (ProxyMethodHandler) proxy.getHandler(); Bean<?> bean = handler.getBean(); Context context = getBeanManager().getContext(bean.getScope()); if (context instanceof AlterableContext) { AlterableContext alterableContext = (AlterableContext) context; alterableContext.destroy(bean); return; } else { throw BeanLogger.LOG.destroyUnsupported(context); } } }
Along with this comment in the org.jboss.weld.bean.ContextualInstanceStrategy class:
/** * ... * For {@link ApplicationScoped} beans a special strategy is used which caches application-scoped bean instances in a volatile field. This implementation respects * the possibility of an instance being destroyed via {@link AlterableContext} and the cached instance is flushed in such case. * ... */
This seems counterintuitive to the abstraction of Instance. I would expect that as the call to Instance.get() didn't necessarily create the @ApplicationScoped bean but created the proxy, the call to Instance.destroy() wouldn't destroy the bean but would destroy the proxy. Is this a bug or as designed?