我爱编程

Spring 绑定有状态对象到线程

2018-04-13  本文已影响104人  ilaoke

问题背景

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

上一篇 下一篇

猜你喜欢

热点阅读