通过HandlerMethodArgumentResolver实现统一添加接口入参参数

背景:项目中有些接口的入参需要用户id信息,最简单的做法在每个Controller方法调用的时候获取登录信息然后给入参设置用户id,但是这样就会有很多重复性的工作。另一个可行的也更好的方案可以使用HandlerMethodArgumentResolver来实现。

部分示例代码如下:

1、需要用户id的入参基类
@Data
@ToString
public class WarehouseBaseReq implements Serializable {
    private String userId;
}
2、新增自定义的解析器处理接口入参设置用户id
@Slf4j
@Component
public class LycMethodArgumentResolver implements HandlerMethodArgumentResolver, InitializingBean {

    @Autowired
    private RequestMappingHandlerAdapter adapter;

    private HandlerMethodArgumentResolver delegate;

    @Override
    public void afterPropertiesSet() throws Exception {
        List<HandlerMethodArgumentResolver> argumentResolvers = adapter.getArgumentResolvers();
        List<HandlerMethodArgumentResolver> resolverList = new ArrayList<>(argumentResolvers);
        // 用自定义的resolver代理
        decorateResolvers(resolverList);
        adapter.setArgumentResolvers(resolverList);
    }

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return delegate.supportsParameter(methodParameter);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Object obj = delegate.resolveArgument(methodParameter,mavContainer,webRequest,binderFactory);
        // 判断只有当入参是指定类型才设置
        if (obj instanceof WarehouseBaseReq) {
            WarehouseBaseReq requestObj = (WarehouseBaseReq)obj;
            // 从ThreadLocal中获取登录用户信息
            UserDto userDto = UserThreadLocal.getUser();
            if (null != userDto) {
            	// 给接口入参设置用户id
                requestObj.setUserId(userDto.getId());
            }
        }
        return obj;
    }

    private void decorateResolvers(List<HandlerMethodArgumentResolver> resolverList){
        for (int i=0; i<resolverList.size();i++){
            HandlerMethodArgumentResolver resolver = resolverList.get(i);
            if (resolver instanceof RequestResponseBodyMethodProcessor){
                this.delegate = resolver;
                resolverList.set(i,this);
                break;
            }
        }
    }
}
3、模拟查询仓库的入参
@Data
@ToString(callSuper = true)
public class WarehouseDto extends WarehouseBaseReq {
    private Long warehouseId;
    private String warehouseName;
    private String warehouseCode;
}
4、模拟查询仓库的controller方法
@RestController
@RequestMapping("/web/warehouse")
public class WarehouseController {
    @Autowired
    private WarehouseBizService warehouseBizService;
    
    @RequestMapping("/testById")
    public BizRsp<WarehouseDto> testQueryById(@RequestBody WarehouseDto warehouseDto) {
        log.info("testQueryById方法执行,warehouseDto:{}",warehouseDto);
        LearingAssert.notNull(warehouseDto.getUserId(),"用户id不能为空");
        return BizRspUtil.success(warehouseBizService.queryById(warehouseDto));
    }
}
5、postman中模拟调用接口,入参只需要传递其他参数,结构如下:

{
“warehouseId”:1
}

自此调用方无需传递用户id,controller的方法也无需写重复代码设置用户id即可实现统一添加接口入参参数的效果。
在spring中还有其他一些处理器,可在一些场景下帮助我们更好的实现一些功能。

参考博客:
[1] spring中的各种处理器
[2]方法参数解析器

你可能感兴趣的:(spring,java,方法参数解析器,spring)