在使用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();
}
}
网友评论