获取请求属性并将其存储以供以后在应用程序中的任何位置使用

时间:2016-01-29 23:45:52

标签: spring spring-mvc

我正在使用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”。

是否有可能实现我的目标?

3 个答案:

答案 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。

参考链接:http://www.baeldung.com/spring-bean-scopes