结论:当请求中不存在isTest,id 等参数,CSE解析时会赋Null值, 不建议在实体类上设置默认值
// 测试用Controler
@RequestMapping("/")
@RestSchema(schemaId = "controller")
public class Controller {
@GetMapping("hello")
public String hello(TestObj testObj) {
System.out.println(testObj.toString());
return "Hello World";
}
}
// 测试QO对象
@Data
public class TestObj {
Boolean isTest = false;
Integer id = 1;
String name;
}
一、ServiceComb 参数解析 Producer处理客户端请求
需要说明的是,一个微服务,如果仅仅对外提供服务就是Provider,如果仅仅是请求其他微服务就是Consumer,两者都涉及,那么这个
微服务,既是Provider又是Consumer,如Edge网关,就是这样,它接收外部的请求,此时是Provider角色,把请求转给下游的其他微服
务,此时是Consumer角色。通常不会存在仅仅是Consumer角色的微服务,因为单纯是Consumer实际应用中没有意义。
请求处理过程
1. Producer服务启动后,会部署vertx服务,监听端口,等待Client连接,并且客户端请求路由配置到VertxRestDispatcher.
2. Client发起请求,请求被vertx路由到VertxRestDispatcher,经过用户配置的HttpServerFilter进行接收请求后的预处理操作。其中
ServerRestArgsFilter是ServiceComb内置的filter,专门用来反序列化请求和序列化响应体的。
3. 经过HttpServerFilter的afterReceiveRequest处理后,正式进入Handler链,进行具体的业务处理,用户可以自定义多个Handler,但最后一个
Handler是ProducerOperationHandler,这个是ServiceComb内置的Handler,它负责通过反射调用请求对应的operation,对请求进行处理,得到
response.
4. 得到response后,回调处理response的方法sendResponseQuietly(resp)
5. 这个方法做的事是,调用HttpServerFilter链上每个filter的beforeSendResponseAsync,进行发送前处理。其中内置的ServerRestArgsFilter会对response对象进行序列化,变成
json字符串,响应给client
ServerRestArgsFilter
1.收到请求解析参数
@Override
public Response afterReceiveRequest(Invocation invocation, HttpServletRequestEx requestEx){
OperationMeta operationMeta = invocation.getOperationMeta();
RestOperationMeta restOperationMeta = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); // 解析参数
Map<String, Object> swaggerArguments = RestCodec.restToArgs(requestEx, restOperationMeta); invocation.setSwaggerArguments(swaggerArguments);
return null;
}
2.发送响应前处理beforeSendResponseAsync
@Override
public CompletableFuture<Void> beforeSendResponseAsync(Invocation invocation, HttpServletResponseEx responseEx) {
...
// 设置响应contentType
responseEx.setContentType(produceProcessor.getName() + "; charset=utf-8");
CompletableFuture<Void> future = new CompletableFuture<>();
try (BufferOutputStream output = new BufferOutputStream(Unpooled.compositeBuffer())) { // 对响应体进行编码
produceProcessor.encodeResponse(output, body); // 得到编码后的byte,设置到responseEx中,用于发送
responseEx.setBodyBuffer(output.getBuffer());
future.complete(null);
} catch (Throwable e) {
...
}
return future;
}
微服务间上下文传递
微服务Consumer与微服务Producer之间,通过rest进行调用,上下文通过调用rest放到header里面实现上下文传递,下面让我们看一下相关代码
-
Consumer invocation的发送: 发送请求前设置header,RestClientInvocation发送处理中setCseContext,header中的key是x-cse-context,代码中的public static final String CSE_CONTEXT = "x-cse-context";
protected void setCseContext() { try { clientRequest.putHeader(Const.CSE_CONTEXT, JsonUtils.writeValueAsString(invocation.getContext())); } catch (Throwable e) { invocation.getTraceIdLogger().error(LOGGER, "Failed to encode and set cseContext, message={}." , ExceptionUtils.getExceptionMessageWithoutTrace(e)); } }
-
Producer 对invocation的恢复:AbstractRestInvocation在调用handler之前,创建invocation,并把Consumer传递过来的上下文恢复到invocation中,见this.setContext();
protected void scheduleInvocation() { try { createInvocation(); } catch (Throwable e) { sendFailResponse(e); return; } try { this.setContext(); } catch (Exception e) { LOGGER.error("failed to set invocation context", e); sendFailResponse(e); return; } ...
-
Producer最后一个Handler-ProducerOperationHandler,doInvoke业务前,设置invocation到线程上下文,业务处理结束后,移除线程上下文中的invocation。所以,在业务处理中,可以通过上下文获取invocation信息。
public void syncInvoke(Invocation invocation, SwaggerProducerOperation producerOperation, AsyncResponse asyncResp) { ContextUtils.setInvocationContext(invocation); Response response = doInvoke(invocation, producerOperation); ContextUtils.removeInvocationContext(); asyncResp.handle(response); }
二、SpringMVC处理请求流程
1. DispatcherServlet中的doDispatcher方法进行响应,并执行至HandlerAdapter调用handle方法进行适配。
2. 来到AbstractHandlerMethodAdapter类调用handleInternal方法,该方法最终被RequestMappingHandlerAdapter所实现,即适配所有被@RequestMapping修饰的Controller的方法。
3. 在RequestMappingHandlerAdapter类中调用invokeHandlerMethod方法,此过程将Controller类中的方法转化为HttpMethod进行解析。
4. 解析后的Method调用invokeAndHandle进行接口的调用访问。
5. 在InvocableHandlerMethod类中的getMethodArgumentValues方法来解析客户端请求传递的数据。在解析之前为每个参数找到合适的参数解析器进行解析,并读取出最终的值。
6. 将读取的值进行返回,返回给Controller端后进行后续的业务逻辑。、
参考引用原文
SpringMVC原理:解析请求参数(源码分析)_pringmvc解析方法参数源码_tracyyy_的博客-CSDN博客