Feign重写URL以及RequestMapping
背景
由于项目采用的是 消费层 + API层(FeignClient) +服务层 的方式。
导致原项目有太多代码冗余复制的地方。例如FeignClient上要写@RequestMapping,
同时要写请求路径,消费层还要写上RequestBody等等,比较麻烦。
遂改进了一下方案,简化日常代码开发的工作量
场景
项目的架构是微服务架构,SpringBoot与SpringCloud均为原生版本。
效果展示
feign层无需写RequestMapping以及RequestBody或者RequestParam等
public interface UserDemoApi {
/**
* 新增用户
* @param userDemo 用户实体
* @return 用户信息
*/
ApiResponse<UserDemo> add(UserDemo userDemo);
/**
* 获取用户信息
* @param userId 用户id
* @param username 用户名
* @return 用户信息
*/
ApiResponse<UserDemo> findByUserIdAndUsername(String userId,String username);
/**
* 用户列表
* @param reqVo 条件查询
* @return 用户列表
*/
ApiResponse<PageResult<UserDemo>> findByCondition(UserDemoReqVo reqVo);
}
@FeignClient(value = "${api.feign.method.value}",fallback = UserDemoFeignApiFallbackImpl.class)
public interface UserDemoFeignApi extends UserDemoApi {
}
整体思路
- 首先要拆成两部分处理,一部分是服务端层,另一部分是客户端层
- 服务端层:主要做的事情就是注册RequestMapping.由于我们的api上是没有写任何注解的,所以我们需要在项目启动的时候把api上的方法都注册上去。另外一个要做的就是,由于我们不写RequestBody,所以要做参数解析配置
- 客户端层:主要做的事情是,项目启动的时候,feign会扫描api上的方法,在它扫描的同时,直接把url定下来存入RequestTemplate中。这样即使后续feign在执行apply没有路径时也不影响feign的正常请求。
实现
Feign的服务端层
1. 继承RequestMappingHandlerMapping,重写getMappingForMethod
- METHOD_MAPPING 为自定义的路径(即RequestMapping的value值)
- 在服务层的实现类中,打上自定义注解@CustomerAnnotation,只有有该注解的实现类才重写RequestMapping。
- 自定义的路径采用的是 类名+方法名
重写的内容
public class CustomerRequestMapping extends RequestMappingHandlerMapping {
private final static String METHOD_MAPPING = "/remote/%s/%s";
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo requestMappingInfo = super.getMappingForMethod(method,handlerType);
if(requestMappingInfo!=null){
if(handlerType.isAnnotationPresent(CustomerAnnotation.class)){
if(requestMappingInfo.getPatternsCondition().getPatterns().isEmpty()||
requestMappingInfo.getPatternsCondition().getPatterns().contains("")){
String[] path = getMethodPath(method, handlerType);
requestMappingInfo = RequestMappingInfo.paths(path).build().combine(requestMappingInfo);
}
}
}else{
if(handlerType.isAnnotationPresent(CustomerAnnotation.class)){
String[] path = getMethodPath(method, handlerType);
requestMappingInfo = RequestMappingInfo.paths(path).methods(RequestMethod.POST).build();
}
}
return requestMappingInfo;
}
private String[] getMethodPath(Method method, Class<?> handlerType) {
Class<?>[] interfaces = handlerType.getInterfaces();
String methodClassNam