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

mad*_*fox 5 spring spring-mvc

我正在使用 Spring MVC,我想从请求标头中获取一些参数,并将它们存储在当前请求期间应用程序中任何位置可用的对象中。想象一个应用程序范围的元数据类。

我想避免使用 RequestContextHolder 因为控制器下面的应用程序的其他部分不应该关心这些值来自请求。它应该是可用的。

我还考虑过使用请求范围的 bean,但随后我必须将它连接到我想要使用它的任何地方。I 反对“只要有它可用”

任何关于从哪里开始的信息都会很棒。

更新:

这是我的想法。我担心当其他请求到来时,他们会设置上一个请求的transactionId。

连接请求作用域 bean:

     <bean id="metaDataContextHolder" class="com.yp.common.context.MetadataContextHolder" scope="request">
        <aop:scoped-proxy/>
     </bean>
Run Code Online (Sandbox Code Playgroud)

这是请求作用域的 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;
    } 
}
Run Code Online (Sandbox Code Playgroud)

捕获要存储的过滤器中的请求标头以供整个应用程序使用:

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

        }
}
Run Code Online (Sandbox Code Playgroud)

然后可以在任何地方访问元数据,而无需再次连接元数据 bean,如下所示:

MetaDataContextHolder.getTransactionId();
Run Code Online (Sandbox Code Playgroud)

更新2

正如我怀疑的那样,MetadataContextHolder 中的静态属性“transactionId”会根据每个请求进行更新。

有可能实现我想做的事情吗?

mad*_*fox 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;
    }

}
Run Code Online (Sandbox Code Playgroud)

然后,我创建了一个过滤器来填充元数据上下文,但这实际上可以填充在任何地方:

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

        }
}
Run Code Online (Sandbox Code Playgroud)

然后,我需要在 web.xml 中注册一个请求侦听器,以便在请求完成时清理本地线程:

<listener>
    <listener-class>com.ws.listener.RequestListener</listener-class>
</listener>
Run Code Online (Sandbox Code Playgroud)

最后我需要在请求侦听器中执行清理:

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) {

    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个很好的元数据对象:

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;
    }   

}
Run Code Online (Sandbox Code Playgroud)