美文网首页我爱编程
Spring 绑定有状态对象到线程

Spring 绑定有状态对象到线程

作者: ilaoke | 来源:发表于2018-04-13 17:29 被阅读104次

    问题背景

    Spring中,在Bean默认为单例的前提下,如果需要在Controller和Services之间频繁传递一个有状态对象时,总是通过方法形参传递,是否不够优雅?如果是,那就continue,如果无所谓,就return.

    解决方法

    使用ThreadLocalTargetSource,设置被代理对象(target bean),每个线程在操作target bean时,都是处理绑定在当前线程的对象,其原理是 代理 + ThreadLocal。

    注意事项

    在request结束时,要清理被代理对象的状态,这里利用filter来实现。

    代码

    配置ThreadLocalTargetSource以及Filter

    @Configuration
    public class AppConfig {
    
        @Bean
        public Filter transactionLogFilter() {
            return new TransactionLogFilter();
        }
    
        @Bean
        public FilterRegistrationBean tenantFilterRegistration() {
            FilterRegistrationBean result = new FilterRegistrationBean();
            result.setFilter(transactionLogFilter());
            result.setUrlPatterns(Collections.singletonList("/*"));
            result.setName("Transaction Log Filter");
            result.setOrder(1);
            return result;
        }
    
        @Bean(destroyMethod = "destroy")
        public ThreadLocalTargetSource threadLocalTenantStore() {
            ThreadLocalTargetSource threadLocalTargetSource = new ThreadLocalTargetSource();
            threadLocalTargetSource.setTargetBeanName("transactionLogStore");
            return threadLocalTargetSource;
        }
    
        @Primary
        @Bean(name = "proxiedThreadLocalTargetSource")
        public ProxyFactoryBean proxiedThreadLocalTargetSource(ThreadLocalTargetSource threadLocalTargetSource) {
            ProxyFactoryBean result = new ProxyFactoryBean();
            result.setTargetSource(threadLocalTargetSource);
            return result;
        }
    
        @Bean(name = "transactionLogStore")
        @Scope(scopeName = "prototype") // 注意此处是prototype,在需要Bean时,为每个线程创建各自的实例
        public TransactionLogStore transactionLogStore() {
            return new TransactionLogStore();
        }
    
    }
    

    Filter

    public class TransactionLogFilter implements Filter {
    
        @Autowired
        TransactionLogStore transactionLogStore;
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String v = request.getParameter("v");
            try {
                transactionLogStore.init(v);
                filterChain.doFilter(servletRequest, servletResponse);
            } finally {
                // 注意此处要清理被代理对象的状态,避免当前线程在被下一个请求使用时保留有上一个请求的状态
                transactionLogStore.clear();
            }
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
    public class TransactionLogStore {
    
        private String logId;
    
        private TTransactionLog transactionLog;
    
        public void init(String v) {
            transactionLog = new TTransactionLog();
            logId = v;
        }
    
        public void clear() {
            logId = null;
            transactionLog = null;
        }
        // ... set get
    }
    

    Ref:
    http://tech.asimio.net/2017/11/28/An-alternative-to-ThreadLocal-using-Spring.html

    相关文章

      网友评论

        本文标题:Spring 绑定有状态对象到线程

        本文链接:https://www.haomeiwen.com/subject/trhhkftx.html