I have an application, that has 2 ApplicationContexts A
and B
, where A
is the parent context of B
. B
overwrites some beans from A
, but also uses some of its beans. For the frontend, I use Wicket and I have two Wicket applications AppA
and AppB
that use the respective Spring ApplicationContext.
The problem now arises with a Session-scoped bean sessionBean
, for which I have 2 different implementations: A
defines a factory method returning an instance of sessionBeanA
, B
has a factory method with same signature returning an instance of sessionBeanB
.
The user now opens AppB
and in the backend, we get an instance of sessionBeanB
, wherever we inject a sessionBean
, as we would expect.
However, if the user now leaves AppB
and opens AppA
(still with the same underlying session), we get an instance of sessionBeanB
injected in A
, because the SessionScope
object still holds the previously created bean object.
How can I prevent that a session-scoped bean leaks in a different ApplicationContext? ApplicationContext A
should not have to bother with beans from B
...
Any help is highly appreciated.
It's possible to extend the SessionScope
from Spring to also consider the ApplicationContext by using a CustomScopeConfigurer
as follows:
import org.springframework.beans.factory.config.CustomScopeConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public final class SessionScopeConfigurer extends CustomScopeConfigurer implements ApplicationContextAware {
private final CustomSessionScope scope;
public SessionScopeConfigurer() {
scope = new CustomSessionScope();
// Overwrite the session scope added by Spring
addScope("session", scope);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
scope.setApplicationContextId(applicationContext.getId());
}
}
where CustomSessionScope
is:
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.web.context.request.SessionScope;
/**
* Extends {@link SessionScope} by modifying all bean names, so that they are specific to their ApplicationContext.
* This avoids session-scoped beans from context a leaking through context b.
*/
public class CustomSessionScope extends SessionScope {
private String applicationContextId;
public void setApplicationContextId(String applicationContextId) {
this.applicationContextId = applicationContextId;
}
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
return super.get(modify(name), objectFactory);
}
@Override
public Object remove(String name) {
return super.remove(modify(name));
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
super.registerDestructionCallback(modify(name), callback);
}
private String modify(String name) {
// Attach ApplicationContextId to the bean name
return name + applicationContextId;
}
}
and you can use it in the Java Spring configuration class with:
@Bean
public static CustomScopeConfigurer configureSessionScope() {
return new SessionScopeConfigurer();
}