我正在使用Spring MVC,我希望从请求头中获取一些参数,并在当前请求期间将它们存储在应用程序中任何位置都可用的对象。想象一下应用程序范围的元数据类。
我想避免使用RequestContextHolder,因为控制器下面的应用程序的其他部分不应该关心值来自请求。它应该是可用的。
我还想过使用一个请求范围的bean,但是我必须在任何想要使用它的地方连接它。我反对“只是让它可用”
任何有关从哪里开始的信息都会很棒。
更新
这是我的想法。我担心当其他请求到来时,他们会设置上一个请求的transactionId。
连接请求范围的bean:
<bean id="metaDataContextHolder" class="com.yp.common.context.MetadataContextHolder" scope="request">
<aop:scoped-proxy/>
</bean>
这是请求范围的bean
public class MetadataContextHolder {
private static String transactionId;
//removed other properties for brevity
public static String getTransactionId() {
return transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
}
捕获要存储的过滤器中的请求标头,以便在整个应用程序中使用:
public class RequestMetatDataFilter implements Filter
{
@Autowired
MetadataContextHolder metaDataHolder;
@Override
public void init(FilterConfig arg0) throws ServletException
{
}
/**
* This filter will add common request metatdata to the MetaDataContextHolder
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain filerChain) throws IOException, ServletException
{
HttpServletRequest request = ((HttpServletRequest)servletRequest);
String transactionId = request.getHeader("transactionId");
metaDataHolder.setTransactionId(transactionId);
...
//store other values
...
filerChain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
然后可以在任何地方访问元数据,而无需再次连接元数据bean,如:
MetaDataContextHolder.getTransactionId();
更新2
正如我所怀疑的,每个请求都会更新MetadataContextHolder中的静态属性“transactionId”。
是否有可能实现我的目标?
答案 0 :(得分:1)
这个答案的两个部分:
我想避免使用RequestContextHolder,因为其他 控制器下面的部分应用程序不应该关心 值来自请求。它应该可用。
是的,这一部分对你刚刚说的理由是有意义的。
我还想过使用一个请求范围的bean,但是我会这样做 把它连接起来我想用它。我反对 &#34;只有它可用&#34;
使用请求范围的bean进行自动连接是这里的方法。当你说你只想让它可用时,它并不完全清楚这意味着什么。听起来你想要一个单例,但它只包含当前请求的数据,无论是谁访问它。请求范围bean设计为仅保存当前请求数据(如您所知),而单例则不是。我实际上会考虑通过注释将bean自动装配为&#34;只是让它可用&#34;。另外,将它作为bean而不是单例使得在需要时更容易测试/模拟。
答案 1 :(得分:0)
要存储请求范围对象并能够以静态方式检索它,我需要实现自己的上下文持有者并将对象存储在本地线程中。
我首先复制RequestContextHolder
中的代码,但我使用Metadata类参数化了我的线程本地对象。
public class MetadataContextHolder {
private static final ThreadLocal<Metadata> metadataHolder =
new NamedThreadLocal<Metadata>("Metadata Context");
private static final ThreadLocal<Metadata> inheritableMetadataHolder =
new NamedInheritableThreadLocal<Metadata>("Metadata Context");
/**
* Reset the metadata for the current thread.
*/
public static void resetMetadata() {
metadataHolder.remove();
}
/**
* Bind the given Metadata to the current thread,
* <i>not</i> exposing it as inheritable for child threads.
* @param metadata the Metadata to expose
* @see #setMetadata(Metadata, boolean)
*/
public static void setMetadata(Metadata metadata) {
setMetadata(metadata, false);
}
/**
* Bind the given Metadata to the current thread.
* @param metadata the Metadata to expose,
* or {@code null} to reset the thread-bound context
* @param inheritable whether to expose the Metadata as inheritable
* for child threads (using an {@link InheritableThreadLocal})
*/
public static void setMetadata(Metadata metadata, boolean inheritable) {
if (metadata == null) {
resetMetadata();
}
else {
if (inheritable) {
inheritableMetadataHolder.set(metadata);
metadataHolder.remove();
}
else {
metadataHolder.set(metadata);
inheritableMetadataHolder.remove();
}
}
}
/**
* Return the Metadata currently bound to the thread.
* @return the Metadata currently bound to the thread,
* or {@code null} if none bound
*/
public static Metadata getMetadata() {
Metadata metadata = metadataHolder.get();
if (metadata == null) {
metadata = inheritableMetadataHolder.get();
}
return metadata;
}
}
然后我创建了一个过滤器来填充元数据上下文,但这可以填充到任何地方:
public class RequestMetatDataFilter implements Filter
{
@Override
public void init(FilterConfig arg0) throws ServletException
{
}
/**
* This filter will add common request metatdata to the MetadataContextHolder
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain filerChain) throws IOException, ServletException
{
HttpServletRequest request = ((HttpServletRequest)servletRequest);
String transactionId = request.getHeader("transactionId");
Metadata metadata = new Metadata(transactionId);
MetadataContextHolder.setMetadata(metadata);
filerChain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
然后我需要在web.xml中注册一个请求监听器,以便在请求完成时清理本地线程:
<listener>
<listener-class>com.ws.listener.RequestListener</listener-class>
</listener>
最后,我需要在请求侦听器中执行清理:
public class RequestListener implements ServletRequestListener {
/**
* Reset the metadata for the current request thread
*/
public void requestDestroyed(ServletRequestEvent event) {
MetadataContextHolder.resetMetadata();
}
/**
* Don't do anything when the request is initialized
*/
public void requestInitialized(ServletRequestEvent event) {
}
}
以下是用于衡量标准的元数据对象:
public class Metadata {
private String idTransaccion;
//Other properties removed for brevity
public Metadata(String idTransaccion) {
super();
this.idTransaccion = idTransaccion;
}
public String getIdTransaccion() {
return idTransaccion;
}
public void setIdTransaccion(String idTransaccion) {
this.idTransaccion = idTransaccion;
}
}
答案 2 :(得分:0)
现在就遇到同样的问题。我需要一个轻松的&amp;每个请求都不引人注意地访问id。我将通过创建一个带有Request Scope注释的Bean,并使用Interceptor / Filter为每个请求填充它来解决它。然后在我需要它的类中自动装配Bean。