Spring sometimes stores variables associated with Request, such as user login information, which have the same life cycle as Request. An easy way to do this is to use ThreadLocal:
public class SecurityContextHolder {
private static final ThreadLocal<SecurityContext> securityContext = new ThreadLocal<SecurityContext>();
public static void set(SecurityContext context) {
securityContext.set(context);
}
public static SecurityContext get() {
return securityContext.get();
}
public static void clear() { securityContext.remove(); }}Copy the code
Use a custom HandlerInterceptor to inject information about it:
@Slf4j
@Component
public class RequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
try {
SecurityContextHolder.set(retrieveRequestContext(request));
} catch (Exception ex) {
log.warn("Failed to read request information", ex);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable
ModelAndView modelAndView) throws Exception {
SecurityContextHolder.clear();
}
Copy the code
In this way, we can use the context directly in the Controller and easily get information about the user:
@Slf4j
@RestController
class Controller {
public Result get() { long userId = SecurityContextHolder.get().getUserId(); / /... }}Copy the code
This method is also used by many blogs. However, there is a subtle catch: the HandlerInterceptor’s postHandle is not always called.
When an Exception appears in Controller:
@Slf4j
@RestController
class Controller {
public Result get() { long userId = SecurityContextHolder.get().getUserId(); / /... throw new RuntimeException(); }}Copy the code
Or an Exception in the HandlerInterceptor’s preHandle:
@Slf4j
@Component
public class RequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
try {
SecurityContextHolder.set(retrieveRequestContext(request));
} catch (Exception ex) {
log.warn("Failed to read request information", ex); } / /... throw new RuntimeException(); / /...return true; }}Copy the code
In these cases, postHandle is not called. This results in the ThreadLocal variable not being cleaned up.
In normal Java environments, ThreadLocal variables can be destroyed along with the Thread itself. However, due to Spring’s thread pool design, the thread responding to the request may always be resident, which results in the variable never being collected by the GC. To make matters worse, a variable that is not properly reclaimed can be threaded into other requests due to thread reuse by the thread pool, directly leading to errors in code logic.
To solve this problem, we can use Spring’s built-in RequestContextHolder, which is the same principle behind ThreadLocal, but it is always cleaned up by the filters of the lower servlets, so there is no leakage.
Here is an example of rewriting using RequestContextHolder:
public class SecurityContextHolder {
private static final String SECURITY_CONTEXT_ATTRIBUTES = "SECURITY_CONTEXT";
public static void setContext(SecurityContext context) {
RequestContextHolder.currentRequestAttributes().setAttribute(
SECURITY_CONTEXT_ATTRIBUTES,
context,
RequestAttributes.SCOPE_REQUEST);
}
public static SecurityContext get() {
return(SecurityContext)RequestContextHolder.currentRequestAttributes() .getAttribute(SECURITY_CONTEXT_ATTRIBUTES, RequestAttributes.SCOPE_REQUEST); }}Copy the code
In addition to using the RequestContextHolder, you can also use the Request Scope Bean or ThreadLocalTargetSource, which is similar in principle.
Keep in mind that ThreadLocal is the equivalent of a static variable inside a thread and is a very leaky point, so use ThreadLocal with extra care.
Welcome Java engineers who have worked for one to five years to join Java Programmer development: 721575865
Group provides free Java architecture learning materials (which have high availability, high concurrency, high performance and distributed, Jvm performance tuning, Spring source code, MyBatis, Netty, Redis, Kafka, Mysql, Zookeeper, Tomcat, Docker, Dubbo, multiple knowledge architecture data, such as the Nginx) reasonable use their every minute and second time to learn to improve yourself, don’t use “no time” to hide his ideas on the lazy! While young, hard to fight, to the future of their own account!