Java RequestContextHolder.getRequestAttributes()
ypp91zr 人气:0相信很多开发过程中都用过RequestContextHolder.getRequestAttributes(),没错,我也经常用,但今天出现了问题,获取到的实例是空的
原因是因为我新开了一个子线程,在子线程调用了RequestContextHolder.getRequestAttributes()。实际获取到的是空的
然后查看了源码
ThreadLocal获取。一个请求到达容器后,Spring会把该请求Request实例通过setRequestAttributes方法 把Request实例放入该请求线程内ThreadLocalMap中,然后就可以通过静态方法取到。原理就是ThreadLocal,但ThreadLocal不能让子线程继承ThreadLocalMap信息,可以使用InherbritableThreadLocal
实现子线程信息传递。
Spring Boot 默认使用ThreadLocal把Request设置进请求线程中,这样如果在请求方法里面另起一个子线程然后再通过getRequestAttributes方法获取,是获取不到的
如果想要在子线程获取,设置inheritable=true即可,但我一直没找到在哪里可以设置,于是自己就写了个工具类来让子线程获取,思路是自定义一个注解,拦截注解,将父线程的ServletRequestAttributes给InheritableThreadLocal,然后在子线程即可获取
package com.shinedata.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RequestInheritableThread { }
AOP
package com.shinedata.aop; import com.shinedata.util.context.RequestHolder; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; import org.springframework.web.context.request.ServletRequestAttributes; @Aspect @Component public class RequestHolderAspect { @Before("@annotation(com.shinedata.annotation.RequestInheritableThread)") public void doBefore(JoinPoint joinPoint) { ServletRequestAttributes servletRequestAttributes = RequestHolder.getServletRequestAttributes(); RequestHolder.setServletRequestAttributes(servletRequestAttributes); } @After("@annotation(com.shinedata.annotation.RequestInheritableThread)") public void doAfter(JoinPoint joinPoint) { RequestHolder.removeServletRequestAttributes(); } }
工具类
package com.shinedata.util.context; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; /** * @ClassName RequestHolder * @Author yupanpan */ public class RequestHolder { private static final Logger logger = LoggerFactory.getLogger(RequestHolder.class); public static final String GET = "GET"; public static final String POST = "POST"; public static final String UTF8 = "UTF-8"; public static InheritableThreadLocal<ServletRequestAttributes> servletRequestAttributesInheritableThreadLocal= new InheritableThreadLocal(); public static HttpServletRequest getRequest() { return getServletRequestAttributes().getRequest(); } public static HttpServletResponse getResponse() { return getServletRequestAttributes().getResponse(); } public static ServletRequestAttributes setServletRequestAttributes(ServletRequestAttributes servletRequestAttributes) { servletRequestAttributesInheritableThreadLocal.set(servletRequestAttributes); return servletRequestAttributes; } public static void removeServletRequestAttributes() { servletRequestAttributesInheritableThreadLocal.remove(); } public static ServletRequestAttributes getServletRequestAttributes() { ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); if(requestAttributes==null){ requestAttributes=servletRequestAttributesInheritableThreadLocal.get(); } return requestAttributes; } }
需要在子线程获取ServletRequestAttributes使用的时候在父方法贴个注解@RequestInheritableThread就行了,父方法里面即使开子线程,子线程里面也能获取ServletRequestAttributes
补充第二种解决办法,在开启新线程之前,将RequestAttributes对象设置为子线程共享
//开启新线程之前,添加代码: ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); RequestContextHolder.setRequestAttributes(sra, true);
加载全部内容