- 背景 当我们项目中好些接口有共同的参数,我们正常都这样抽成
@Data
public class PublicParam {
/**用户ID*/
private Long userId;
}
@Data
public class CreateCartDTO extends PublicParam {
/**产品ID*/
private Long productId;
/**产品skuID*/
private Long skuId;
/**购买数量*/
private Integer num = 1;
}
但是当我们的调用链路很长的时候,我特么拒绝这么做,来回的传参数太烦了
所以我决定这么做
- 解决方案
封装的公共参数上下文
public class ParamContext {
public static final String USER_ID = "userId";
public static final String IP = "ip";
public static final String VERSION = "version";
public static final String CLIENT = "client";
/**
* 参数缓存
*/
private static final ThreadLocal<HashMap<String, Object>> cache =
ThreadLocal.withInitial(HashMap::new);
/**
* 数据清理
*/
public static void clean() {
cache.remove();
}
public static Long getUserId() {
return null != cache.get().get(USER_ID) ? Long.valueOf(toString(cache.get().get(USER_ID))) : null;
}
public static void setUserId(String userId) {
if (!StringUtils.hasLength(userId)) {
return;
}
cache.get().put(USER_ID, Long.valueOf(userId));
}
private static String toString(Object o) {
if (null == o) {
return null;
}
return String.valueOf(o);
}
}
识别AOP的注解,个人偏向于注解,好用,不要那些*号点来点去,容易出错
@Retention(RUNTIME)
@Target(METHOD)
public @interface PublicParamFlag {
}
封装的切面
注意为了防止内存泄露,我加了finally
@Aspect
@Component
public class PublicParamAspect {
@Pointcut("@annotation(com.roy.annotation.PublicParamFlag)")
private void paramPointCut() {
}
@Around(value = "paramPointCut()")
public Object around(ProceedingJoinPoint pjt) throws Throwable {
try {
Object[] args = pjt.getArgs();
PublicParam publicParam = (PublicParam) args[0];
Long userId = publicParam.getUserId();
// 设置各参数到ThreadLocal的cache中
ParamContext.setUserId(String.valueOf(userId));
return pjt.proceed();
} finally {
// very import,必须调用clean方法进行ThreadLocal变量的remove
ParamContext.clean();
}
}
}
- controller和service中的运用
@RequestMapping("/user4")
@RestController
@Slf4j
public class UserController4 {
@RequestMapping("/save")
@PublicParamFlag
public String save(@RequestBody CreateCartDTO dto) {
System.out.println(JSON.toJSONString(dto));
Long userId = ParamContext.getUserId();
return String.valueOf(userId);
}
}
- 总结
利用ThreadLocal在一条链路上进行参数的传播,并且不写传参,参数太多、链路太长就可以这么做。
- 其实写的代码还挺多的,那我发现了spring框架其实针对这个轮子是有做封装的,接下来给大家演示一下了,上demo
@RequestMapping("/save/{id}")
public String save(@PathVariable Integer id) {
RequestContextHolder.currentRequestAttributes().setAttribute("id", id, RequestAttributes.SCOPE_REQUEST);
return requestService.getStr();
}
@Component
public class RequestService {
public String getStr(){
Object id = RequestContextHolder.getRequestAttributes().getAttribute("id", RequestAttributes.SCOPE_REQUEST);
return "id:" + id;
}
}
- 轮子总结
虽然我没有把RequestContextHolder.currentRequestAttributes().setAttribute("id", id, RequestAttributes.SCOPE_REQUEST);
这个写在aop中,但也是实现了controller和service之间进行参数的传递