前言
在一个web系统中,可能会经常需要处理一些公共的参数,比如,在一个Saas系统中,需要根据用户选择的租户信息来展示不同的数据,这个时候前端可能就会将这个租户信息放在了header中,后端怎么去解析这个租户信息呢?
方法有很多种,下面就来介绍几种具体的做法。
解决方案
直接通过HttpServletRequest参数,进行方法的传递,毫无疑问,这种方式是最简单的,不过缺点也显而易见,就是很麻烦,显得不够优雅。如果方法嵌套层次比较多,都会要携带这个参数,多少优点累赘感。
参考代码如下
package com.tml.mouseDemo.controller;
import com.tml.mouseDemo.constants.CommonResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@Slf4j
public class TestController {
@GetMapping("/common")
public CommonResponse<String> commonHandler(HttpServletRequest request) {
String tenantId = request.getHeader("tenantId");
log.info("tenantId: {}", tenantId);
return CommonResponse.success("common handler");
}
}
解决这种多层次方法参数的传递一般会使用ThreadLocal,那么我们是不是在web系统增加一个拦截器或者过滤器,拦截http请求,将http请求中的租户信息放进threadLocal中,当然可以,不过这种方案会存在一个问题,就是需要手动清理threadLocal对象中的数据以防止内存泄漏。
HttpServlertRequest对象的作用域是请求作用域(Request Scope)。这意味着每个HTTP请求都会创建一个新的HttpServlertRequest对象,并且该对象仅在当前请求的生命周期内有效。请求结束时,HttpServlertRequest对象会被销毁。
基于HttpServlertRequest的作用域,这里给出两种方案仅供参考
RequestContextHolder静态获取
直接看代码
package com.tml.mouseDemo.controller;
import com.tml.mouseDemo.constants.CommonResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@RestController
@Slf4j
public class TestController {
@GetMapping("/common")
public CommonResponse<String> commonHandler() {
String tenantId = getTenantId();
log.info("tenantId: {}", tenantId);
return CommonResponse.success("common handler");
}
private static String getTenantId(){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
return request.getHeader("tenantId");
}
}
可以看到,使用RequestContextHolder是直接静态获取,去看源码,发现里面也是使用了ThreadLocal、InheritableThreadLocal,和预想的一致,使用这种方式的优点就是,可以在任意地方获取租户信息,而不需要传递httpServletRequest对象
直接注入httpServletRequest
直接看代码
package com.tml.mouseDemo.controller;
import com.tml.mouseDemo.constants.CommonResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@Slf4j
public class TestController {
@Autowired
private HttpServletRequest request;
@GetMapping("/common")
public CommonResponse<String> commonHandler() {
String tenantId = request.getHeader("tenantId");
log.info("tenantId: {}", tenantId);
return CommonResponse.success("common handler");
}
}
使用这种方式的优点显而易见,就是简单,在被spring容器纳入管理的对象上,就可以随意注入httpServletRequest对象,而这个对象的作用域是Request Scope,使用者根本不用担心不同的请求会造成数据混乱。
总结
上面的两个demo可以发现,使用RequestContextHolder更加的通用,可以在任意地方调用静态方法获取header中的数据,不过,在spring项目中,被spring管理的对象中,我们也可以通过直接注入的方式使用httpServletRequest对象,这种方式是最简洁的。