SpringMVC 执行流程
- 当用户通过浏览器发起一个HTTP请求,请求直接到前端控制器DispatcherServlet;
- 前端控制器接收到请求以后调用处理器映射器HandlerMapping,处理器映射器根据请求的URL找到具体的Handler,并将它返回给前端控制器;
- 前端控制器调用处理器适配器HandlerAdapter去适配调用Handler;
- 处理器适配器会根据Handler去调用真正的处理器去处理请求,并且处理对应的业务逻辑;
- 当处理器处理完业务之后,会返回一个ModelAndView对象给处理器适配器,HandlerAdapter再将该对象返回给前端控制器;这里的Model是返回的数据对象,View是逻辑上的View。
- 前端控制器DispatcherServlet将返回的ModelAndView对象传给视图解析器ViewResolver进行解析,解析完成之后就会返回一个具体的视图View给前端控制器。(ViewResolver根据逻辑的View查找具体的View)
- 前端控制器DispatcherServlet将具体的视图进行渲染,渲染完成之后响应给用户(浏览器显示)。
RequestBody 解析
源码基于 springboot 2.3.12
- http 请求处理入口:DispatcherServlet#doDispatch
- 请求具体处理流程:RequestMappingHandlerAdapter#handleInternal
- 实际处理:ServletInvocableHandlerMethod#invokeAndHandle
- InvocableHandlerMethod#invokeForRequest
- 参数解析:InvocableHandlerMethod#getMethodArgumentValues
- @RequestBody 最终由 RequestResponseBodyMethodProcessor#resolveArgument 负责解析
ResponseBody 解析
- 参考上面 @RequestBody 的解析,InvocableHandlerMethod#invokeForRequest 处理请求完成后,处理后的结果由 HandlerMethodReturnValueHandlerComposite#handleReturnValue
- @RequestBody 最终由 RequestResponseBodyMethodProcessor#handleReturnValue 负责解析
实战应用
背景
dubbo 应用升级到 springcloud,支持 dubbo + http 双协议发布,对外提供给 Feign 客户端,原 dubbo 应用接口均继承自 BaseFacade 接口,请求参数和响应结果分别是泛型 BaseRequest、BaseResponse
改造
-
同 BaseFacade,定义针对 Feign 客户端的统一接口 BaseFacadeFeign,业务接口均继承自该接口
javapublic interface BaseFacadeFeign<Q extends BaseRequest, P extends BaseResponse> { String APP_NAME = "online-service"; @PostMapping P execute(@RequestBody Q request); } @FeignClient(value = BaseFacadeFeign.APP_NAME, path = "/queryAcctInfo") public interface QueryAcctInfoFacadeFeign extends BaseFacadeFeign<QueryAcctInfoRequest, QueryAcctInfoResponse> { }
-
根据 Feign 接口定义,动态实现 http 服务发布
总体思路:
- 通过 Feign 接口定义,将其请求路径映射为 http 请求地址
- http 请求的具体 Handler 由 Facade 的具体实现来处理
Feign 接口 和 Facade 接口分别定义不同的包:com.xxx.feign、com.xxx.facade,需要先解析所有的 Feign、Facade接口,类扫描基类:
java
@RequiredArgsConstructor
public abstract class ClassFilePatternResolver {
private final String patten;
@SneakyThrows
public Set<Class<?>> resole() {
Set<Class<?>> classSet = Sets.newHashSet();
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(patten);
// MetadataReader 的工厂类
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resolver);
for (Resource resource : resources) {
// 用于读取类信息
MetadataReader reader = readerFactory.getMetadataReader(resource);
// 扫描到的class
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
if (!clazz.isInterface()) {
continue;
}
if (support(clazz)) {
classSet.add(clazz);
}
}
return classSet;
}
protected abstract boolean support(Class<?> clazz);
}
Feign 接口扫描:
java
public class FeignClassFilePatternResolver extends ClassFilePatternResolver {
public FeignClassFilePatternResolver() {
super(String.format("classpath:%s/**/*.class",
ClassUtils.convertClassNameToResourcePath("com.xxx.feign")));
}
@Override
public boolean support(Class<?> clazz) {
return !BaseFacadeFeign.class.equals(clazz) && BaseFacadeFeign.class.equals(clazz.getInterfaces()[0])
&& AnnotationUtils.isAnnotationPresent(clazz, FeignClient.class);
}
}
Facade 接口扫描:
java
public class FacadeClassFilePatternResolver extends ClassFilePatternResolver {
public FacadeClassFilePatternResolver() {
super(String.format("classpath:%s/**/*.class",
ClassUtils.convertClassNameToResourcePath("com.xxx.facade")));
}
@Override
protected boolean support(Class<?> clazz) {
return !BaseFacade.class.equals(clazz) && BaseFacade.class.equals(clazz.getInterfaces()[0]);
}
}
http 服务动态发布:
java
@Configuration
@RequiredArgsConstructor
public class DynamicMappingConfig {
private static final String SUFFIX = "Feign";
private final ApplicationContext context;
private final RequestMappingHandlerMapping mapping;
private final RequestMappingHandlerAdapter handlerAdapter;
private final List<HttpMessageConverter<?>> messageConverters;
@PostConstruct
public void init() {
OnlineRequestResponseMethodProcessor processor = new OnlineRequestResponseMethodProcessor(messageConverters);
handlerAdapter.setArgumentResolvers(Collections.singletonList(processor));
handlerAdapter.setReturnValueHandlers(Collections.singletonList(processor));
Set<Class<?>> feignClassSet = new FeignClassFilePatternResolver().resole();
Set<Class<?>> facadeClassSet = new FacadeClassFilePatternResolver().resole();
feignClassSet.forEach(clazz -> {
Class<?> facadeClass = findFacadeClass(clazz, facadeClassSet);
FeignClient annotation = clazz.getAnnotation(FeignClient.class);
String path = annotation.path();
if (StringUtils.isEmpty(path)) {
throw new ValidateException(String.format("接口[%s]未定义请求路径", clazz));
}
RequestMappingInfo mappingInfo = RequestMappingInfo.paths(path)
.consumes(MediaType.APPLICATION_JSON_VALUE)
.produces(MediaType.APPLICATION_JSON_VALUE)
.methods(RequestMethod.POST).build();
Object handler = context.getBean(facadeClass);
// Class<?>[] genetics = GenericTypeResolver.resolveTypeArguments(facadeClass, BaseFacade.class);
// Class<?> paramType = genetics[0];
// Class<?> returnType = genetics[1];
Method method = Optional.ofNullable(ReflectionUtils.findMethod(facadeClass, "execute", (Class<?>) null))
.orElseThrow(() -> new ValidateException(String.format("根据接口[%s]未获取到execute方法", facadeClass)));
mapping.registerMapping(mappingInfo, handler, method);
});
}
private Class<?> findFacadeClass(Class<?> feignClass, Set<Class<?>> facadeClassSet) {
String facadeClassShortName = ClassUtils.getShortName(feignClass);
String prefix = facadeClassShortName.substring(0, facadeClassShortName.length() - SUFFIX.length());
return facadeClassSet.stream()
.filter(facadeClass -> ClassUtils.getShortName(facadeClass).contains(prefix))
.findFirst()
.orElseThrow(() -> new ValidateException(String.format("根据Feign接口[%s]未获取到对应Facade接口[%s]",
feignClass, prefix)));
}
}
由于上面动态发布使用 @RequestBody、@ResponseBody,但其实际需要用到 RequestResponseBodyMethodProcessor
解析,因此需要重写 RequestResponseBodyMethodProcessor
java
public class OnlineRequestResponseMethodProcessor extends RequestResponseBodyMethodProcessor {
public OnlineRequestResponseMethodProcessor(List<HttpMessageConverter<?>> converters) {
super(converters);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
Type type = GenericTypeResolver.resolveType(parameter.getGenericParameterType(), BaseFacade.class);
return BaseRequest.class.equals(type);
}
@Override
public boolean supportsReturnType(MethodParameter parameter) {
Type type = GenericTypeResolver.resolveType(parameter.getGenericParameterType(), BaseFacade.class);
return BaseResponse.class.equals(type);
}
}
小结
实现动态发布 http 服务
- RequestMappingHandlerMapping 实现 http 服务的注册
- RequestMappingHandlerAdapter 注册自定义参数、响应结果的解析