问题: 我该如何告诉Spring一组具有自定义作用域的bean应该被视为垃圾,以便同一线程上的下一个请求不会重复使用它们的状态?
我的做法: 我在Spring中实现了一个自定义作用域,以模仿请求作用域(HttpRequest)的生命周期,但针对TcpRequests。它与这里找到的内容非常相似。
我发现的许多自定义作用域示例都是原型或单例的变体,没有显式终止bean的发生,或者基于线程本地或ThreadScope,但它们没有描述告诉Spring生命周期已经结束并且所有bean都应该被销毁。
我尝试过的事情(可能不正确):
事件+监听器用于指示范围的开始和结束(这些发生在收到消息时和在响应发送之前);在监听器中,范围被明确清除,从而清除线程本地实现所使用的整个映射(scope.clear())。在测试中手动处理时,清除范围确实会导致下一次调用context.getBean()返回一个新实例,但我的bean在单例类中自动装配,不会得到新的bean-它一遍又一遍地使用相同的bean。
实现BeanFactoryPostProcessor、BeanPostProcessor、BeanFactoryAware、DisposableBean的监听器,并尝试在所有可销毁的bean实例上调用destroy();类似于this,但仅适用于我的自定义作用域。这似乎失败了,因为没有什么明确地结束bean的生命周期,尽管我在接收范围结束事件时调用了customScope.clear();结束范围似乎并不意味着“结束与此范围相关的所有bean”。
我已经广泛阅读了Spring文档,似乎很清楚Spring不管理这些自定义bean的生命周期,因为它不知道何时或如何销毁它们,这意味着必须告诉它何时以及如何销毁它们;我已经尝试阅读和理解Spring提供的会话和请求范围,以便我可以模拟这个过程,但我缺少一些东西(再次强调,由于这不是一个Web感知应用程序,并且我没有使用HttpRequests,因此这些对我不可用,这是我们应用程序结构的非平凡变化)。
请问有没有人能帮我指明正确的方向?
我有以下的代码示例:
XML上下文配置:
<int-ip:tcp-connection-factory id="serverConnectionFactory" type="server" port="19000"
serializer="javaSerializer" deserializer="javaDeserializer"/>
<int-ip:tcp-inbound-gateway id="inGateway" connection-factory="serverConnectionFactory"
request-channel="incomingServerChannel" error-channel="errorChannel"/>
<int:channel id="incomingServerChannel" />
<int:chain input-channel="incomingServerChannel">
<int:service-activator ref="transactionController"/>
</int:chain>
TransactionController(处理请求):
@Component("transactionController")
public class TransactionController {
@Autowired
private RequestWrapper requestWrapper;
@ServiceActivator
public String handle(final Message<?> requestMessage) {
// object is passed around through various phases of application
// object is changed, things are added, and finally, a response is generated based upon this data
tcpRequestCompletePublisher.publishEvent(requestWrapper, "Request lifecycle complete.");
return response;
}
}
TcpRequestScope(范围定义):
@Component
public class TcpRequestScope implements Scope {
private final ThreadLocal<ConcurrentHashMap<String, Object>> scopedObjects =
new InheritableThreadLocal<ConcurrentHashMap<String, Object>>({
@Override
protected ConcurrentHashMap<String, Object> initialValue(){
return new ConcurrentHashMap<>();
}
};
private final Map<String, Runnable> destructionCallbacks =
Collections.synchronizedMap(new HashMap<String, Runnable>());
@Override
public Object get(final String name, final ObjectFactory<?> objectFactory) {
final Map<String, Object> scope = this.scopedObjects.get();
Object object = scope.get(name);
if (object == null) {
object = objectFactory.getObject();
scope.put(name, object);
}
return object;
}
@Override
public Object remove(final String name) {
final Map<String, Object> scope = this.scopedObjects.get();
return scope.remove(name);
}
@Override
public void registerDestructionCallback(final String name, final Runnable callback) {
destructionCallbacks.put(name, callback);
}
@Override
public Object resolveContextualObject(final String key) {
return null;
}
@Override
public String getConversationId() {
return String.valueOf(Thread.currentThread().getId());
}
public void clear() {
final Map<String, Object> scope = this.scopedObjects.get();
scope.clear();
}
}
TcpRequestCompleteListener:
@Component
public class TcpRequestCompleteListener implements ApplicationListener<TcpRequestCompleteEvent> {
@Autowired
private TcpRequestScope tcpRequestScope;
@Override
public void onApplicationEvent(final TcpRequestCompleteEvent event) {
// do some processing
// clear all scope related data (so next thread gets clean slate)
tcpRequestScope.clear();
}
}
RequestWrapper(我们在请求生命周期中使用的对象):
@Component
@Scope(scopeName = "tcpRequestScope", proxyMode =
ScopedProxyMode.TARGET_CLASS)
public class RequestWrapper implements Serializable, DisposableBean {
// we have many fields here which we add to and build up during processing of request
// actual request message contents will be placed into this class and used throughout processing
@Override
public void destroy() throws Exception {
System.out.print("Destroying RequestWrapper bean");
}
}
@TransactionController
实例,因此每个人都可能改变同一个对象。即使使用ThreadLocal,属性也绑定到线程生命周期,而不是请求。如果您想要请求上下文,我建议在ServiceActivator
方法或@MessageMapping
上使用@Header
注释进行注入,这非常灵活。 - stringy05@MessageMapping
和@Header
的内容。我想强调的最重要的部分是,这不是一个面向Web的应用程序,所以我会确保你提到的选项适用于我。非常感谢你迄今为止的帮助。 - Andrew Cotton