Feign原理实现
- 手写RPC框架Feign
- 什么是RPC
- Feign注入原理
- EnableFeignClients
- Feign调用入口
- 手写Feign
手写RPC框架Feign
阅读本文你可获得:
1、RPC原理
2、feign注入原理
3、如何手写feign框架
4、动态代码设计模式应用场景
什么是RPC
RPC:远程过程调用,也就是我们通常说“HTTP”调用,也有人说dubbo不是基于HTTP也是RPC啊,那怎么理解这件事呢?其实RPC就是指两服务之间调用不再写繁琐代码,有个东西把这件事包装起来,像写普通方法一样调用远程服务。
有人从网络分层(5层)角度出发说:feign不属于RPC,dubbo才是真正的RPC,feign底层是HTTP(应用层)或7层调用,dubbo是4层(传输层)调用,真正PRC是4层而不是7层。也有人说RPC就是HTTP,在此小编认为两者说的不准确,RPC不是区分实现过程(4层、7层)只区分调用手段(像调用普通service一样)。
从宏观角度讲只要是开发人员调用外部服务像调用方法一样的框架都叫RPC。
Feign注入原理
使用feign需要在启动类上加@EnableFeignClients注解并写上包名,我们在类上加@FeignClient注解、方法加@GetMapping等注解并配置相应属性feign架构就起作用,那么从这里思考这几个注解起了什么作用?我们倒着分析。
- GetMapping、PostMapping 这几个注解同Controller一样这里不再描述;
- FeignClient 是对我们类名、请求地址进行统一配置这里不再描述;
- EnableFeignClients 这个注解相当重要且它是核心注解,以上两个注解是否起作用,怎么发起HTTP调用都是该注解在工作;
EnableFeignClients
注解上有一个@Import(FeignClientsRegistrar.class)
核心代码在FeignClientsRegistrar类中。
该类实现接口ImportBeanDefinitionRegistrar
它是Spring扩展点之一,支持我们写的代码封装成BeanDefinition
俗称bean,bean的有功能它都有例如:@Autowired注解该bean、postProcessAfterInitialization
bean的后置初始化等功能。
- registerBeanDefinitions 方法中两个方法:registerDefaultConfiguration注册默认配置和registerFeignClients注册feign的配置,我们注入feign的配置所以核心在registerFeignClients方法中。
scanner
是项目启动扫描所有class文件,并设置过滤条件(类上有FeignClient
注解),且通过metadata
(启动类上EnableFeignClients
注解配置包名)并获得注解所扫描对应包名。
拿到启动类上所有包类,过滤上一步设置好的条件(带FeignClient
注解的)类,找到我们定义FeignClient
接口类。
上图四步feign帮我们做了,其实第三步对于我们手写框架来说可以忽略,重要看第四步。
第一步:构建FeignClientFactoryBean
并通过构造函数设置值(生成代理核心)。
第二步:获得bean别名、并设置bean是否是主要bean。
第三步:向Spring中注册bean。
致此我们写的带FeignClient
注解的接口注册到spring中,可以通过@Autowired
或@Resource
等方式注入到代码里开始使用,那么有的小伙伴就会问我的实现还没写呢?调用逻辑呢?
Feign调用入口
Java动态代理分为两种:JDK和CGLIB,它两本质区别是有没有实现接口,JDK必须是实现接口,CGLIB可以不用实现接口。feign用的是JDK动态代理所以我们写代码时FeignClient
注解要放在接口,刚才注入bean过程也进行了判断,非接口不让注解会报错(第三张图有分析)。
我们最后一张图有说:构建FeignClientFactoryBean
并通过构造函数设置值,这里是产生代理核心。该主要实现了FactoryBean
返回值就是我们注册到spring中bean的类型,在该类中可以注入其它bean,感觉兴趣的小伙伴可以从源一直点下去,在newInstance类中会生成代理对象,如下图。
在动态代理类中编写具体实现代码。
以上核对源码分析完成,我们分析源码主要目的是为实现简单feign做铺垫。
手写Feign
如果我们要写一个简单版本的feign那应该怎么做呢?
1、我们需要把接口类注入到spring中。
2、在动态代理中编写实现方法。
听着很简单实际操作也很简单(>‿<)
- 定义启动类注解
MonkeyEnableHttpClient
并配置扫描包同EnableFeignClients
注解,我们只需要value一个注解即可。
/*** @Author: LailaiMonkey* @Description:* @Date:Created in 2022/09/15 4:11 下午* @Modified By:*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MonkeyHttpClientRegister.class)
public @interface MonkeyEnableHttpClient {String[] value();
}
- 定义接口类注解
MonkeyHttpClient
同FeignClient
注解,需要知道bean名称(通过反射自动生成)、该类配置全局url、超时时间等信息。
/*** @Author: LailaiMonkey* @Description:* @Date:Created in 2022/09/15 2:48 下午* @Modified By:*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MonkeyHttpClient {/*** 注册bean名称,默认类名** @return*/String name() default "";/*** 调用url** @return*/String url();/*** 读超时时间** @return*/String readTimeout() default "5000";/*** 连接超时时间** @return*/String connectTimeout() default "5000";}
- 定义注册类
MonkeyHttpClientRegister
启动时扫描包并注册到Spring中。
/*** @Author: LailaiMonkey* @Description:* @Date:Created in 2022/09/15 4:04 下午* @Modified By:*/
@Slf4j
public class MonkeyHttpClientRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware, BeanClassLoaderAware, ResourceLoaderAware {private Environment environment;private ClassLoader classLoader;private ResourceLoader resourceLoader;@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {this.classLoader = classLoader;}@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(MonkeyHttpClient.class);scanner.addIncludeFilter(annotationTypeFilter);Map<String, Object> attributes = metadata.getAnnotationAttributes(MonkeyEnableHttpClient.class.getCanonicalName());if (CollectionUtils.isEmpty(attributes)) {return;}Set<String> basePackages = new HashSet<>();for (String pkg : (String[]) attributes.get("value")) {if (StringUtils.hasText(pkg)) {basePackages.add(pkg);}}for (String basePackage : basePackages) {Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();Assert.isTrue(annotationMetadata.isInterface(), "@GZHttpClient can only be specified on an interface");Map<String, Object> feignClientAttributeMap = annotationMetadata.getAnnotationAttributes(MonkeyHttpClient.class.getCanonicalName());if (CollectionUtils.isEmpty(feignClientAttributeMap)) {return;}String className = annotationMetadata.getClassName();Class<?> clazz = null;try {clazz = Class.forName(className);} catch (ClassNotFoundException e) {log.error("httpClient start up fail:", e);}String beanName = className.substring(className.lastIndexOf(".") + 1);String alias = beanName.substring(0, 1).toLowerCase().concat(beanName.substring(1)).concat("HttpClient");String name = String.valueOf(feignClientAttributeMap.get("name"));String url = String.valueOf(feignClientAttributeMap.get("url"));if (!StringUtils.isEmpty(name)) {alias = name;}String readTimeout = String.valueOf(feignClientAttributeMap.get("readTimeout"));String connectTimeout = String.valueOf(feignClientAttributeMap.get("connectTimeout"));BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(MonkeyHttpClientFactoryBean.class);definition.addConstructorArgValue(clazz);definition.addConstructorArgValue(url);definition.addConstructorArgValue(readTimeout);definition.addConstructorArgValue(connectTimeout);definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);AbstractBeanDefinition handleDefinition = definition.getBeanDefinition();handleDefinition.setPrimary(true);// 向Spring的上下文中注册bean组件BeanDefinitionHolder holder = new BeanDefinitionHolder(handleDefinition, className, new String[]{alias});BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}}}}protected ClassPathScanningCandidateComponentProvider getScanner() {return new ClassPathScanningCandidateComponentProvider(false, this.environment) {@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {if (beanDefinition.getMetadata().isIndependent()) {if (beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().getInterfaceNames().length == 1&& Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) {try {Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(), MonkeyHttpClientRegister.this.classLoader);return !target.isAnnotation();} catch (Exception ex) {this.logger.error("Could not load target class: " + beanDefinition.getMetadata().getClassName(), ex);}}return true;}return false;}};}
}
- 定义工厂类
MonkeyHttpClientFactoryBean
生成动态代理。
/*** @Author: LailaiMonkey* @Description:* @Date:Created in 2022/9/15 7:14 下午* @Modified By:*/
public class MonkeyHttpClientFactoryBean implements FactoryBean<Object>, InitializingBean {private Class<?> clazz;private String url;private Integer readTimeout;private Integer connectTimeout;public MonkeyHttpClientFactoryBean(Class<?> clazz, String url, Integer readTimeout, Integer connectTimeout) {this.clazz = clazz;this.url = url;this.readTimeout = readTimeout;this.connectTimeout = connectTimeout;}@Overridepublic void afterPropertiesSet() {if (Objects.isNull(clazz)) {throw new MonkeyHttpException("http start up param error, class is null");}if (StringUtils.isBlank(url)) {throw new MonkeyHttpException("http start up param error, url is null");}if (Objects.isNull(readTimeout)) {throw new MonkeyHttpException("http start up param error, readTimeout is null");}if (Objects.isNull(connectTimeout)) {throw new MonkeyHttpException("http start up param error, connectTimeout is null");}}@Overridepublic Object getObject() {return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},new MonkeyHttpClientExecute(url, readTimeout, connectTimeout));}@Overridepublic Class<?> getObjectType() {return clazz;}}
- 定义代理类
MonkeyHttpClientExecute
这是最重要一步!!!我们接口实现逻辑全在这里面。
/*** @Author: 侯旭日* @Description:* @Date:Created in 2022/1/2 9:21 下午* @Modified By:*/
@Slf4j
public class GZHttpClientExecute implements InvocationHandler {private static final Map<Method, GZHttpClientParamModel> CACHE_PARAM_MODEL = new HashMap<>();private static final Map<Method, GZHttpClientResultModel> CACHE_RESULT_MODEL = new HashMap<>();private static final Map<Method, RetryTemplate> CACHE_RETRY_MAP = new HashMap<>();private static final Map<Class<? extends Throwable>, Boolean> RETRY_CONFIG_MAP = new HashMap<>();static {RETRY_CONFIG_MAP.put(GZHttpException.class, true);RETRY_CONFIG_MAP.put(SocketTimeoutException.class, true);}private CloseableHttpClient httpClient;private String url;private String appKey;private String appSecret;private Integer readTimeout;private Integer connectTimeout;private Integer retry;private HttpSignature httpSignature;private EtcdPropertiesClient etcdPropertiesClient;public GZHttpClientExecute(CloseableHttpClient httpClient, EtcdPropertiesClient etcdPropertiesClient, HttpSignature httpSignature,String url, String appKey, String appSecret,Integer readTimeout, Integer connectTimeout, Integer retry) {this.httpClient = httpClient;this.etcdPropertiesClient = etcdPropertiesClient;this.httpSignature = httpSignature;this.url = url;this.appKey = appKey;this.appSecret = appSecret;this.readTimeout = readTimeout;this.connectTimeout = connectTimeout;this.retry = retry;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {GZRequestMapping requestMapping = AnnotatedElementUtils.getMergedAnnotation(method, GZRequestMapping.class);String errorInfo = "call " + method.getDeclaringClass().getName() + "#" + method.getName();if (Objects.isNull(requestMapping)) {throw new GZHttpException(errorInfo + " not http method");}// 生成参数Map<String, Object> paramMap = createParam(method, args);// 填充uriString path = fillUri(paramMap, requestMapping.path());// 缓存返回值模型cacheResultMode(method);// 处理ModelAttributehandlerModelAttribute(method, args, requestMapping, paramMap);// retryhandlerRetry(method, getRetry(requestMapping));String strUrl = getUrl(url, path);// 获得form-dataList<NameValuePair> formData = getFormData(method, args, requestMapping);// 不是form-data则获得bodyString body = StringUtils.EMPTY;if (CollectionUtils.isEmpty(formData)) {body = getBody(method, args, requestMapping);// 兼容老逻辑,有body且不是泛型则放uri上if (StringUtils.isNotBlank(body) && Boolean.FALSE.equals(CACHE_PARAM_MODEL.get(method).getGeneric())) {Map<String, Object> bodyMap = JsonUtil.of(body, TreeMap.class);strUrl += "?" + bodyMap.entrySet().stream().map(e -> e.getKey() + "=" + (Objects.nonNull(e.getValue()) ? UrlEncoded.encodeString(e.getValue().toString()) : StringUtils.EMPTY)).collect(Collectors.joining("&"));} else {strUrl += httpSignature.urlSignatureParam(paramMap, appKey, appSecret);}}// // 非get请求且普通参数需放form-data,get请求参数需放url上
// if (!RequestMethod.GET.equals(requestMapping.method()) && StringUtils.isBlank(body) && CollectionUtils.isEmpty(formData)) {
// httpSignature.urlSignatureParam(paramMap, appKey, appSecret);
// for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
// formData.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
// }
// } else if (RequestMethod.GET.equals(requestMapping.method())) {
// // 参数签名
// strUrl += httpSignature.urlSignatureParam(paramMap, appKey, appSecret);
// }// 获得监控uriString monitorUri = getMonitorUri(url, requestMapping.path());// 请求requestRequest request = setRequest(requestMapping, formData, strUrl, errorInfo, body, monitorUri);String result = sendHttp(method, request, formData, errorInfo, requestMapping, strUrl, paramMap, body);return deserializationResult(method, result);}/*** 获得from-data** @param method* @param args* @param requestMapping* @return*/private List<NameValuePair> getFormData(Method method, Object[] args, GZRequestMapping requestMapping) {List<NameValuePair> nameValuePairs = new ArrayList<>();if (HttpMediaType.MULTIPART_FORM_DATA_VALUE.equals(requestMapping.mediaType())) {switch (requestMapping.method()) {case POST:case PUT:case DELETE:GZHttpClientParamModel paramModel = CACHE_PARAM_MODEL.get(method);// 有body体if (Objects.nonNull(paramModel.getGenericIndex()) && Boolean.FALSE.equals(paramModel.getGeneric())) {Map<String, Object> bodyMap = JsonUtil.of(JsonUtil.toJson(args[paramModel.getGenericIndex()]), TreeMap.class);Map<String, Object> signatureMap = httpSignature.bodySignatureParam(bodyMap, appKey, appSecret);for (Map.Entry<String, Object> entry : signatureMap.entrySet()) {nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));}}break;default:return nameValuePairs;}}return nameValuePairs;}/*** 获得request** @param requestMapping* @param formData* @param strUrl* @param errorInfo* @param body* @return*/private Request setRequest(GZRequestMapping requestMapping, List<NameValuePair> formData, String strUrl, String errorInfo, String body, String monitorUri) {Request request;switch (requestMapping.method()) {case GET:request = Request.get(strUrl);break;case POST:request = Request.post(strUrl);setRequest(request, formData, body);break;case PUT:request = Request.put(strUrl);setRequest(request, formData, body);break;case DELETE:request = Request.delete(strUrl);setRequest(request, formData, body);break;default:throw new GZHttpException(errorInfo + " request method error");}request.responseTimeout(Timeout.ofMilliseconds(getReadTimeout(requestMapping))).connectTimeout(Timeout.ofMilliseconds(getConnectTimeout(requestMapping))).addHeader(HttpConstants.METRIC_HTTP_URL_HEADER, monitorUri);return request;}private Request setRequest(Request request, List<NameValuePair> formData, String body) {if (CollectionUtils.isNotEmpty(formData)) {request = request.bodyForm(formData, StandardCharsets.UTF_8);} else {request = request.bodyString(body, ContentType.APPLICATION_JSON);}return request;}/*** 请求http** @param method* @param request* @param errorInfo* @param requestMapping* @param strUrl* @param paramMap* @param body* @return* @throws Throwable*/private String sendHttp(Method method, Request request, List<NameValuePair> formData, String errorInfo, GZRequestMapping requestMapping,String strUrl, Map<String, Object> paramMap, String body) throws Throwable {String result;if (CACHE_RETRY_MAP.containsKey(method)) {RetryTemplate retryTemplate = CACHE_RETRY_MAP.get(method);result = retryTemplate.execute((RetryCallback<String, Throwable>) context -> {String res;try {res = request.execute(httpClient).handleResponse(new GaiaHttpClientResponseHandler(true));if (StringUtils.isBlank(res)) {throw new GZHttpException(errorInfo + " timeout, return is null");}} catch (Exception e) {log.info("call url error:{}, param:{}, body:{}, retryCount:{}, exception:{}", strUrl, JsonUtil.toJson(paramMap), body, context.getRetryCount() + 1, e);throw e;}return res;});} else {try {result = request.execute(httpClient).handleResponse(new GaiaHttpClientResponseHandler(true));} catch (Exception e) {log.info("call url error:{}, param:{}, body:{}, exception:{}", strUrl, JsonUtil.toJson(paramMap), body, e);throw new GZHttpException(errorInfo + " call url: " + strUrl + " error, reason:" + e.getMessage());}}String strFormData = formData.stream().map(m -> m.getName() + "=" + m.getValue()).collect(Collectors.joining(","));if (requestMapping.showLog()) {log.info("call url:{}, param:{}, form-data:{}, body:{}, result:{}", strUrl, JsonUtil.toJson(paramMap), strFormData, body, result);}return result;}/*** 处理ModelAttribute** @param method* @param args* @param requestMapping* @param paramMap*/private void handlerModelAttribute(Method method, Object[] args, GZRequestMapping requestMapping, Map<String, Object> paramMap) {// get请求处理model参数if (RequestMethod.GET.equals(requestMapping.method())) {GZHttpClientParamModel paramModel = CACHE_PARAM_MODEL.get(method);if (Objects.nonNull(paramModel.getGenericIndex()) && Boolean.FALSE.equals(paramModel.getGeneric())) {Object param = args[paramModel.getGenericIndex()];if (param instanceof Map) {paramMap.putAll((Map) param);} else {paramMap.putAll(JsonUtil.of(JsonUtil.toJson(param), Map.class));}}}}/*** 生成参数*/private Map<String, Object> createParam(Method method, Object[] args) {GZHttpClientParamModel paramModel = cacheParamModel(method, args);Map<String, Object> paramMap = new TreeMap<>();for (int i = 0; i < paramModel.getParamNames().size(); i++) {String paramName = paramModel.getParamNames().get(i);if (Objects.isNull(args[i]) || args[i].getClass().getName().startsWith("java.lang")) {paramMap.put(paramName, args[i]);} else if (args[i] instanceof Object[]) {StringBuilder str = new StringBuilder();for (Object o : (Object[]) args[i]) {str.append(o.toString()).append(",");}str.delete(str.length() - 1, str.length());paramMap.put(paramName, str);} else if (args[i] instanceof Map) {paramMap.putAll((Map) args[i]);}}return paramMap;}/*** 缓存入参模型** @param method* @param args*/private GZHttpClientParamModel cacheParamModel(Method method, Object[] args) {GZHttpClientParamModel paramModel = CACHE_PARAM_MODEL.get(method);if (Objects.nonNull(paramModel)) {return paramModel;}return safeCacheParamModel(method, args);}private synchronized GZHttpClientParamModel safeCacheParamModel(Method method, Object[] args) {GZHttpClientParamModel paramModel = CACHE_PARAM_MODEL.get(method);if (Objects.nonNull(paramModel)) {return paramModel;}Parameter[] parameters = method.getParameters();List<String> paramNames = new ArrayList<>();Integer index = null;for (int i = 0; i < parameters.length; i++) {// 获得别名RequestParam requestParam = AnnotatedElementUtils.getMergedAnnotation(parameters[i], RequestParam.class);String paramName = parameters[i].getName();if (Objects.nonNull(requestParam)) {paramName = requestParam.value();}paramNames.add(paramName);// 不是基础类型或数组则为model(Map等均为model)if (!(Objects.isNull(args[i]) || args[i].getClass().getName().startsWith("java.lang") ||args[i] instanceof Object[])) {index = i;}}boolean isGeneric = false;if (Objects.nonNull(index)) {Class<?> parameterType = method.getParameterTypes()[index];if (parameterType.isAssignableFrom(List.class)) {isGeneric = true;}}paramModel = new GZHttpClientParamModel(paramNames, index, isGeneric);CACHE_PARAM_MODEL.put(method, paramModel);return paramModel;}/*** 缓存返回值模型*/private void cacheResultMode(Method method) {if (CACHE_RESULT_MODEL.containsKey(method)) {return;}safeCacheResultMode(method);}private synchronized void safeCacheResultMode(Method method) {if (CACHE_RESULT_MODEL.containsKey(method)) {return;}Type returnType = method.getGenericReturnType();CACHE_RESULT_MODEL.put(method, new GZHttpClientResultModel(returnType));}/*** 处理重试** @param method* @param retry*/private void handlerRetry(Method method, int retry) {if (retry > 0) {if (CACHE_RETRY_MAP.containsKey(method)) {return;}safeCacheRetry(method, retry);}}private synchronized void safeCacheRetry(Method method, int retry) {SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(retry, RETRY_CONFIG_MAP);RetryTemplate template = new RetryTemplate();template.setRetryPolicy(retryPolicy);CACHE_RETRY_MAP.put(method, template);}/*** 填充uri*/private String fillUri(Map<String, Object> paramMap, String path) {StringBuilder sb = new StringBuilder(path);String[] split = sb.toString().split("/");sb.delete(0, sb.length());for (String str : split) {if (StringUtils.isBlank(str)) {continue;}if (str.startsWith("{") && str.endsWith("}")) {String replace = str.replace("{", "").replace("}", "");if (paramMap.containsKey(replace)) {Object value = paramMap.get(replace);str = Objects.isNull(value) ? StringUtils.EMPTY : value.toString();}}sb.append("/").append(str);}return sb.toString();}/*** 获得监控uri*/private String getMonitorUri(String url, String path) {StringBuilder sb = new StringBuilder(path);String[] split = sb.toString().split("/");sb.delete(0, sb.length());for (String str : split) {if (StringUtils.isBlank(str) || str.startsWith("{") && str.endsWith("}")) {continue;}sb.append("/").append(str);}return url + sb.toString();}/*** 反序列化结果** @param method* @param result* @return*/private Object deserializationResult(Method method, String result) {if (StringUtils.isBlank(result)) {throw new GZHttpException("call " + method.getDeclaringClass().getName() + "#" + method.getName() + " timeout, return is null");}Type returnType = CACHE_RESULT_MODEL.get(method).getReturnType();if (Objects.equals(String.class, returnType)) {return result;}return JsonUtil.of(result, new TypeReference<Object>() {@Overridepublic Type getType() {return returnType;}});}/*** 获得body体** @param method* @param args* @return*/private String getBody(Method method, Object[] args, GZRequestMapping requestMapping) {switch (requestMapping.method()) {case POST:case PUT:case DELETE:GZHttpClientParamModel paramModel = CACHE_PARAM_MODEL.get(method);// 有body体if (Objects.nonNull(paramModel.getGenericIndex())) {if (Boolean.TRUE.equals(paramModel.getGeneric())) {return JsonUtil.toJson(args[paramModel.getGenericIndex()]);} else {Map<String, Object> bodyMap = JsonUtil.of(JsonUtil.toJson(args[paramModel.getGenericIndex()]), TreeMap.class);return JsonUtil.toJson(httpSignature.bodySignatureParam(bodyMap, appKey, appSecret));}}break;default:return StringUtils.EMPTY;}return StringUtils.EMPTY;}/*** 获得请求url** @param url* @param path* @return*/private String getUrl(String url, String path) {if (url.endsWith("/")) {return url + path.substring(1);}return url + path;}/*** 获得读超时时间** @param requestMapping* @return*/private int getReadTimeout(GZRequestMapping requestMapping) {int connReadTimeout = readTimeout;int timeout = etcdPropertiesClient.getTimeout(requestMapping.readTimeout());if (timeout > 0) {connReadTimeout = timeout;}return connReadTimeout;}/*** 获得连接超时时间** @param requestMapping* @return*/private int getConnectTimeout(GZRequestMapping requestMapping) {int connConnectTimeout = connectTimeout;int timeout = etcdPropertiesClient.getTimeout(requestMapping.connectTimeout());if (timeout > 0) {connConnectTimeout = timeout;}return connConnectTimeout;}/*** 获得重试次数** @param requestMapping* @return*/private int getRetry(GZRequestMapping requestMapping) {int retryCnt = retry;if (requestMapping.retry() > 0) {retryCnt = requestMapping.retry();}return retryCnt;}
}
执行类小编写了部分伪代码仅供参考相信大家读到该文章技术水平差不了,因为我们是自研框架所以调用逻辑或参数生成都需要我们自己处理,小编写了部分参数处理逻辑如果你们有更好的想法可以扩展也可评论留下联系方式添加小编微信(LailaiMonkeyJava)
demo以RestTemplate作为http发送工具进行测试,调用get请求获得用户信息。
demo地址