是否可以创建一个工厂或代理来决定线程是在(Web)Request还是后台进程(即scheduler)中运行,然后根据这些信息创建会话bean还是原型bean?
示例(伪Spring配置:)
<bean id="userInfoSession" scope="session" />
<bean id="userInfoStatic" scope="prototype" />
<bean id="currentUserInfoFactory" />
<bean id="someService" class="...">
<property name="userInfo" ref="currentUserInfoFactory.getCurrentUserInfo()" />
</bean>
我希望这能让我的问题更容易理解。。。
我的解决方案
更新自己的问题永远不会太迟;)。我用两个不同的客户端会话实例解决了这个问题,一个是SessionScoped客户端会话,另一个是SingletonScoped会话。两者都是普通的豆子。
<bean id="sessionScopedClientSession" class="com.company.product.session.SessionScopedClientSession" scope="session">
<aop:scoped-proxy />
</bean>
<bean id="singletonScopedClientSession" class="com.company.product.session.SingletonScopedClientSession" />
<bean id="clientSession" class="com.company.product.session.ClientSession">
<property name="sessionScopedClientSessionBeanName" value="sessionScopedClientSession" />
<property name="singletonScopedClientSessionBeanName" value="singletonScopedClientSession" />
</bean>
然后,ClientSession将决定是单例作用域还是会话作用域:
private IClientSession getSessionAwareClientData() {
String beanName = (isInSessionContext() ? sessionScopedClientSessionBeanName : singletonScopedClientSessionBeanName);
return (IClientSession) ApplicationContextProvider.getApplicationContext().getBean(beanName);
}
可以通过以下方式收集会话类型:
private boolean isInSessionContext() {
return RequestContextHolder.getRequestAttributes() != null;
}
所有类都实现了一个名为IClientSession的接口。singletonScoped和sessionScoped bean都是从找到实现的BaseClientSession扩展而来的。
然后每个服务都可以使用客户端会话,即:
@Resource
private ClientSession clientSession;
...
public void doSomething() {
Long orgId = clientSession.getSomethingFromSession();
}
现在,如果我们更进一步,我们可以为会话编写类似Emulator的东西。这可以通过初始化clientSession(它不在请求的上下文中)单例会话来完成。现在,所有服务都可以使用相同的clientSession,我们仍然可以“模拟”用户,即:
clientSessionEmulator.startEmulateUser( testUser );
try {
service.doSomething();
} finally {
clientSessionEmulator.stopEmulation();
}
还有一个建议:注意SingletonScoped clientSession实例中的线程处理!哦,我想我可以用更少的台词来完成它;)如果你想了解更多关于这种方法的信息,请随时与我联系。