feign传递数组_SpringCloud实战⼩技巧(解决feignGET传
pojo、继承。。。
spring cloud 实践
项⽬结构
config 配置中⼼
端⼝:8888,⽅便起见直接读取配置⽂件,⽣产环境可以读取git。application-dev.properties为全局配置。先启动配置中⼼,所有服务的配置(包括注册中⼼的地址)均从配置中⼼读取。
eureka 注册中⼼
端⼝:8761,/metadata端点实现metadata信息配置。
zuul ⽹关
端⼝:8080,演⽰解析token获得label并放⼊header往后传递
core 框架核⼼包
核⼼jar包,所有微服务均引⽤该包,使⽤AutoConfig实现免配置,模拟⽣产环境下spring-cloud的使⽤。
starter spring cloud以及core框架依赖简化starter
provider 服务提供者
api 服务提供者SDK
强制服务消费⽅只能通过服务提供者提供的SDK包(api项⽬)进⾏调⽤,以便更加⽅便控制服务消费⽅的⾏为:
提供可能的优化,SDK直接读取缓存(SDK只读缓存,写缓存放还是在微服务)。
统计有哪些服务消费者,info端点显⽰包依赖,通过注册中⼼遍历所有服务。
⽅便dubbo平滑迁移,接⼝加feign注解。
service 服务提供者微服务
端⼝:18090,服务提供者,⽆特殊逻辑。
consumer 服务消费者
端⼝:18090,调⽤服务提供者。
Restful API 设计规范
/版本/访问控制/域对象
/版本/访问控制/域对象/action/动作
版本
版本为微服务级别,也就是说不存在⼀个API是v3版,其他API还只是v1版的问题,要升级所有API版本⼀起升级,但是需要保证之前版本v1-v3还可以使⽤。
原则上要兼容上⼀个版本 如果当前是 /v3 则 /v2 要求可以正常使⽤ /v1 不做要求
如果⽆法兼容 需要通知所有服务消费者 并约定版本⽕车 ⼀起上线时间
⼩技巧
下⾯swagger注解就可以实现上述要求,路径中的{version}没有限定,其实可以是任意内容,通过swagger⽂档来进⾏约定
// v1版api 即将废弃
@ApiOperation("分页查询")
@RequestMapping(value = "/v1/pb/product", method = RequestMethod.GET)
@Deprecated
List selectAll(@RequestParam("offset") Integer offset, @RequestParam("limit") Integer limit);
//ProviderApiAutoConfig.CURRENT_VERSION="v2" ⽤来替换上⾯的v1版本
@ApiOperation("带过滤条件和排序的分页查询")
@RequestMapping(value = "/{version}/pb/product", method = RequestMethod.GET)
@ApiImplicitParam(name = "version", paramType = "path", allowableValues = ProviderApiAutoConfig.CURRENT_VERSION, required = true)
Response> selectAllGet(Page page);
// ProviderApiAutoConfig.COMPATIBLE_VERSION="v2,v1" swagger-ui上会显⽰⼀个version的下拉框, 默认v2
@ApiOperation(value = "带过滤条件和排序的复杂分页查询")
@ApiImplicitParam(name = "version", paramType = "path", allowableValues =
ProviderApiAutoConfig.COMPATIBLE_VERSION, required = true)
@RequestMapping(value = "/{version}/pb/product/action/search", method = RequestMethod.POST)
Response> selectAll(@RequestBody Page page);
访问控制
⽤于在⽹关统⼀进⾏访问控制, 访问控制分为
pb - public 所有请求均可访问
pt - protected 需要进⾏token认证通过后⽅可访问
df - default ⽹关进⾏token认证 并且请求参数和返回结果进⾏加解密
pv - private ⽆法通过⽹关访问 只能微服务内部调⽤
其他 同pv
action
⽆法⽤名词+请求⽅法表述的可以扩展为 /域对象/action/动词 必须为POST⽅法
例如 POST /users/action/login
问题描述
主要由于使⽤了API(SDK)为了偷懒,以及Restful API路径中的版本带来的⼀系列问题。
spring MVC 不⽀持继承接⼝中⽅法参数上的注解(⽀持继承类、⽅法上的注解)
API中为了⽅便,使⽤feign替代RestTemplate⼿动调⽤。带来的问题:springMVC注解想偷懒,只在feign接⼝写⼀遍,然后实现类继承此接⼝即可。
例如:
feign接⼝定义如下
@FeignClient(ProviderApiAutoConfig.PLACE_HOLD_SERVICE_NAME)
public interface ProductService {
// 为了让spring mvc能够正确绑定变量
public class Page extends PageRequest {
}
@RequestMapping(value = "/{version}/pt/product", method = RequestMethod.POST)
Response insert(@RequestBody Product product);
}
service实现类⽅法参数必须再写⼀次@RequestBody注解,⽅法上的@RequestMapping注解可以省略
@RestController
public class ProductServiceImpl implements ProductService {
@Override
public Response insert(@RequestBody Product product) {
product.setId(1L);
return new Response(product);
}
}
解决办法,@Configuration配置类添加如下代码,扩展spring默认的ArgumentResolvers
public static MethodParameter interfaceMethodParameter(MethodParameter parameter, Class annotationType) { if (!parameter.hasParameterAnnotation(annotationType)) {
for (Class> itf : DeclaringClass().getInterfaces()) {
try {
Method method = Method().getName(), Method().getParameterTypes()); MethodParameter itfParameter = new MethodParameter(method, ParameterIndex());
if (itfParameter.hasParameterAnnotation(annotationType)) {
return itfParameter;
}
} catch (NoSuchMethodException e) {
continue;
}
}
}
return parameter;
}
@PostConstruct
public void modifyArgumentResolvers() {
List list = new ArrayList<>(ArgumentResolvers());
list.add(0, new PathVariableMethodArgumentResolver() { // PathVariable ⽀持接⼝注解
@Override
public boolean supportsParameter(MethodParameter parameter) {
return super.supportsParameter(interfaceMethodParameter(parameter, PathVariable.class));
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
ateNamedValueInfo(interfaceMethodParameter(parameter, PathVariable.class));
}
});
list.add(0, new RequestHeaderMethodArgumentResolver(beanFactory) { // RequestHeader ⽀持接⼝注解
@Override
public boolean supportsParameter(MethodParameter parameter) {
return super.supportsParameter(interfaceMethodParameter(parameter, RequestHeader.class));
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
ateNamedValueInfo(interfaceMethodParameter(parameter, RequestHeader.class));
}
});
list.add(0, new ServletCookieValueMethodArgumentResolver(beanFactory) { // CookieValue ⽀持接⼝注解
@Override
public boolean supportsParameter(MethodParameter parameter) {
return super.supportsParameter(interfaceMethodParameter(parameter, CookieValue.class));
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
ateNamedValueInfo(interfaceMethodParameter(parameter, CookieValue.class));
}
});
list.add(0, new MessageConverters()) { // RequestBody ⽀持接⼝注解@Override
public boolean supportsParameter(MethodParameter parameter) {
return super.supportsParameter(interfaceMethodParameter(parameter, RequestBody.class));
}
@Override
protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) { // ⽀持@Valid验证super.validateIfApplicable(binder, interfaceMethodParameter(methodParam, Valid.class));
}
});
/
/ 修改ArgumentResolvers, ⽀持接⼝注解
adapter.setArgumentResolvers(list);
}
swagger不⽀持继承接⼝中⽅法参数上的注解(⽀持继承类、⽅法上的注解)
没有到swagger⾃带扩展点能够优雅扩展的⽅法,只好修改源码了,下载springfox-spring-web 2.8.0 release源码包。添加lspringmvc的注解有哪些
xmlns:xsi="/2001/XMLSchema-instance"
4.0.0
io.springfox
springfox-spring-web
2.8.0-charles
jar
1.8
@
UTF-8
UTF-8
${java.version}
${java.version}
org.springframework.boot
spring-boot-dependencies
1.5.10.RELEASE
pom
import
org.springframework.boot
spring-boot-starter-web
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论