如何托管ThreadLocal的声明周期来防止内存泄露

在使用ThreadLocal时,必须显式的调用remove方法,以防止内存泄露。有没有一种更加简便的方式去隐式的调用remove方法?

定义注解:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ThreadLocalLifeCycle {
}

定义BeanPostProcessor:

@Slf4j
@Component
public class ThreadLocalLifeCycleBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {

    private Class<? extends Annotation> threadLocalLifeCycleType = ThreadLocalLifeCycle.class;

    private int order = Ordered.LOWEST_PRECEDENCE - 1;

    private List<ThreadLocal> cache = new ArrayList<>();

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        Class<?> targetClass = bean.getClass();
        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            field.setAccessible(true);
            AnnotationAttributes ann = findThreadLocalLifeCycleAnnotation(field);
            if (ann != null) {
                Object o = field.get(bean);
                if (!(o instanceof ThreadLocal)) {
                    throw new RuntimeException("ThreadLocalLifeCycle is only used on the ThreadLocal property!");
                }
                cache.add((ThreadLocal) o);
            }
        });
        return bean;
    }

    public List<ThreadLocal> getCache() {
        return cache;
    }

    @Override
    public int getOrder() {
        return this.order;
    }

    private AnnotationAttributes findThreadLocalLifeCycleAnnotation(AccessibleObject ao) {
        if (ao.getAnnotations().length > 0) {
            AnnotationAttributes attributes =
                    AnnotatedElementUtils.getMergedAnnotationAttributes(ao, threadLocalLifeCycleType);
            if (attributes != null) {
                return attributes;
            }
        }
        return null;
    }
}

BeanPostProcessor的目的是在Spring初始bean的生命周期中,对bean进行处理,上面的代码时解析bean的Field(包括static方法,然后将其放入缓存中,以便在Filter中进行统一的remove)

拦截器:

@Slf4j
@WebFilter
public class ThreadLocalClearFilter implements Filter {

    @Autowired
    private ThreadLocalLifeCycleBeanPostProcessor threadLocalLifeCycleBeanPostProcessor;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        chain.doFilter(request, response);
        List<ThreadLocal> cache = threadLocalLifeCycleBeanPostProcessor.getCache();
        cache.forEach(r -> {
            log.info("开始移除数据{}.", r.get());
            r.remove();
        });
    }

    @Override
    public void destroy() {

    }
}

使用方式:

@Slf4j
@RestController
public class ClearThreadLocalController {

    @ThreadLocalLifeCycle
    private static ThreadLocal<String> STRING_THREAD_LOCAL = new ThreadLocal<>();

    @GetMapping(value = "/cr/t1")
    public String tt() {
        STRING_THREAD_LOCAL.set("aaa");
        return STRING_THREAD_LOCAL.get();
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容