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

2022-03-01  本文已影响0人  小胖学编程

在使用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();
    }
}
上一篇 下一篇

猜你喜欢

热点阅读