首页 > 其他分享 >仿@FeignClient实现使用Http请求外部服务

仿@FeignClient实现使用Http请求外部服务

时间:2023-08-15 23:24:07浏览次数:36  
标签:FeignClient Http String class public return null method 请求

因为某些原因,原本注册在同一个nacos里的部分微服务需要拆分出去,而拆分出去的那部分服务调用方式需要修改。所以为了简单省事,加个了@HttpClient注解用来替换@FeignClient。

三步走:

  1、@HttpClient注解

  2、扫描被@HttpClient注解的接口

  3、为扫描到的接口创建代理类

@HttpClient注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface HttpClient {

    HttpUrl value();
}

url枚举类

@AllArgsConstructor
@Getter
public enum HttpUrl {
    BAI_DU("https://www.baidu.com/", "百度"),

    private String url;
    private String desc;
}

扫描被@HttpClient注解的接口

@Component
public class HttpClientRegistryProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware {

    private ResourceLoader resourceLoader;

    private Environment environment;

    private final RestTemplate restTemplate;

    public HttpClientRegistryProcessor() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(5000);//ms
        factory.setConnectTimeout(15000);//ms
        restTemplate = new RestTemplate(factory);
    }

    @Override
    public void postProcessBeanDefinitionRegistry(@NotNull BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        scanner.addIncludeFilter(new AnnotationTypeFilter(HttpClient.class));
        Set<BeanDefinition> beanDefinitionHolders = scanner.findCandidateComponents("org.jeecg");
        for (BeanDefinition holder : beanDefinitionHolders) {
            registerHttpClientBean(holder, registry);
        }
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private void registerHttpClientBean(BeanDefinition definition, BeanDefinitionRegistry registry) {
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) definition;
        String beanClassName = beanDefinition.getBeanClassName();
        try {
            Class<?> targetClass = Class.forName(beanClassName);
            if (targetClass.isInterface()) {
                Object proxy = Proxy.newProxyInstance(
                        targetClass.getClassLoader(),
                        new Class[]{targetClass},
                        new HttpClientInvocationHandler(targetClass, restTemplate));
                BeanDefinitionBuilder proxyBeanBuilder = BeanDefinitionBuilder.genericBeanDefinition(targetClass, (Supplier) () -> proxy);
                AbstractBeanDefinition proxyBeanDefinition = proxyBeanBuilder.getRawBeanDefinition();
                String beanName = BeanDefinitionReaderUtils.generateBeanName(definition, registry);
                registry.registerBeanDefinition(beanName, proxyBeanDefinition);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void postProcessBeanFactory(@NotNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // Do nothing
    }

    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(@NotNull AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }

    @Override
    public void setEnvironment(@NotNull Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(@NotNull ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

 

为扫描到的接口创建代理类

public class HttpClientInvocationHandler implements InvocationHandler {

    private final RestTemplate restTemplate;
    private final String baseUrl;

    private static final List<MediaType> DEFAULT_MEDIA_TYPES = Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN);

    public static final Method HASH_CODE;
    public static final Method EQUALS;
    public static final Method TO_STRING;

    static {
        Class<Object> object = Object.class;
        try {
            HASH_CODE = object.getDeclaredMethod("hashCode");
            EQUALS = object.getDeclaredMethod("equals", object);
            TO_STRING = object.getDeclaredMethod("toString");
        } catch (NoSuchMethodException e) {
            // Never happens.
            throw new Error(e);
        }
    }

    private static final CopyOptions copyOptions = new CopyOptions(null, true);

    static {
        Editor<String> fieldNameEditor = s -> {
            if ("msg".equals(s)) {
                return "message";
            }
            return s;
        };
        copyOptions.setFieldNameEditor(fieldNameEditor);
    }


    public HttpClientInvocationHandler(Class<?> targetClass, RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
        HttpClient httpClientAnnotation = targetClass.getAnnotation(HttpClient.class);
        if (httpClientAnnotation != null) {
            this.baseUrl = httpClientAnnotation.value().getUrl();
        } else {
            this.baseUrl = null;
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.equals(HASH_CODE)) {
            return objectHashCode(proxy);
        }
        if (method.equals(EQUALS)) {
            return objectEquals(proxy, args[0]);
        }
        if (method.equals(TO_STRING)) {
            return objectToString(proxy);
        }
        // 对接口默认方法的支持
        if (method.isDefault()) {
            Class<?> declaringClass = method.getDeclaringClass();
            Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
            constructor.setAccessible(true);
            return constructor.
                    newInstance(declaringClass, MethodHandles.Lookup.PRIVATE).
                    unreflectSpecial(method, declaringClass).
                    bindTo(proxy).
                    invokeWithArguments(args);
        }

        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(DEFAULT_MEDIA_TYPES);
        String url = resolveUrl(baseUrl, method);
        Object body = null;
        HttpMethod httpMethod = null;
        if (method.isAnnotationPresent(GetMapping.class)) {
            httpMethod = HttpMethod.GET;
        } else if (method.isAnnotationPresent(PostMapping.class)) {
            httpMethod = HttpMethod.POST;
        } else if (method.isAnnotationPresent(PutMapping.class)) {
            httpMethod = HttpMethod.PUT;
        } else if (method.isAnnotationPresent(DeleteMapping.class)) {
            httpMethod = HttpMethod.DELETE;
        }
        if (httpMethod == null) {
            throw new RuntimeException("@HttpClient不支持的请求方式");
        }
        UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(url);
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            RequestParam requestParamAnnotation = AnnotationUtils.findAnnotation(parameter, RequestParam.class);
            if (requestParamAnnotation != null) {
                String name = requestParamAnnotation.value();
                name = !"".equals(name) ? name : parameter.getName();
                uriComponentsBuilder.queryParam(name, args[i]);
                continue; // 应该不可能同一个参数同时标注多个吧。
            }
            RequestBody requestBodyAnnotation = AnnotationUtils.findAnnotation(parameter, RequestBody.class);
            if (requestBodyAnnotation != null) {
                body = args[i]; // 应该没人会传两个body吧
                continue;
            }
            RequestHeader requestHeaderAnnotation = AnnotationUtils.findAnnotation(parameter, RequestHeader.class);
            if (requestHeaderAnnotation != null) {
                String name = requestHeaderAnnotation.value();
                name = !"".equals(name) ? name : parameter.getName();
                headers.set(name, String.valueOf(args[i]));
                continue;
            }
        }
        HttpEntity<?> httpEntity = new HttpEntity<>(body, headers);
        ResponseEntity<String> exchange = restTemplate.exchange(uriComponentsBuilder.toUriString(), httpMethod, httpEntity, String.class);
        String res = exchange.getBody();
        JSONObject jsonObject = JSON.parseObject(res);
        Map<String, Object> innerMap = jsonObject.getInnerMap();
        Class<?> returnType = method.getReturnType();
        return BeanUtil.mapToBean(innerMap, returnType, false, copyOptions);
    }

    private String resolveUrl(String baseUrl, Method method) {
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        if (getMapping != null) {
            String path = getMapping.value()[0];
            return baseUrl + path;
        }
        PostMapping postMapping = method.getAnnotation(PostMapping.class);
        if (postMapping != null) {
            String path = postMapping.value()[0];
            return baseUrl + path;
        }
        PutMapping putMapping = method.getAnnotation(PutMapping.class);
        if (putMapping != null) {
            String path = putMapping.value()[0];
            return baseUrl + path;
        }
        DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
        if (deleteMapping != null) {
            String path = deleteMapping.value()[0];
            return baseUrl + path;
        }
        throw new RuntimeException("@HttpClient不支持的请求方式");
    }

    public String objectClassName(Object obj) {
        return obj.getClass().getName();
    }

    public int objectHashCode(Object obj) {
        return System.identityHashCode(obj);
    }

    public boolean objectEquals(Object obj, Object other) {
        return obj == other;
    }

    public String objectToString(Object obj) {
        return objectClassName(obj) + '@' + Integer.toHexString(objectHashCode(obj));
    }
}

 

使用方式

直接替换掉@FeignClient

//@FeignClient(name = "xxx-service")
@HttpClient(HttpUrl.BAI_DU)

标签:FeignClient,Http,String,class,public,return,null,method,请求
From: https://www.cnblogs.com/end-of-a-journey/p/17632700.html

相关文章

  • Http面试2
    Http21.网络协议是什么在计算机网络要做到井井有条的交换数据,就必须遭守―些事先约定好的规则;比如交换数据的格式.是否需要发送一个应答信息。这些规则被称为网络协议。2.为什么要对网络协议分层简化问题难度和复杂度。由于各层之间独立,我们可以分割大问题为小问题。灵活性......
  • Http面试1
    Http11.http协议的请求方式是什么?HTTP是一个基于TCP/IP通信协议来传递数据,包括html文件、图像、结果等,即是一个客户端和服务器端请求和应答的标准。基本上用到的就是GET和POST,充其量再遇到个option请求。2.http和https有什么区别?https有ca证书,http一般没有;http是超......
  • ThingsKit物联网平台设备HTTP接入
    入门介绍HTTP基础知识HTTP是一种通用网络协议,可用于物联网应用程序。HTTP协议基于TCP,并使用请求-响应模型。ThingsKit服务器节点充当支持HTTP和HTTPS协议的HTTP服务器。对于一些非常单一的应用场景,比如只需要定期采集上报数据,不论是快速开发原型,还是小规模的应用,设备使用HTTP......
  • Firefox浏览器怎么设置HTTP代理
    Firefox浏览器是广受欢迎的开源浏览器,提供了丰富而灵活的功能。通过设置HTTP代理,我们可以实现隐私保护、突破网络限制或加速网页加载速度。下面,让我们一步步了解如何在Firefox浏览器中设置HTTP代理,让网络浏览更加自由与安心。第一步:打开Firefox浏览器的设置页面首先,打开Firefox浏览......
  • 动态HTTP代理与搜索引擎优化(SEO)的关系
     作为一名专业的爬虫代理供应者,今天我要和大家聊一聊动态HTTP代理与搜索引擎优化(SEO)之间的关系。你可能会觉得这两个话题没有直接联系,但实际上它们是息息相关的。在这篇文章中,我将向大家解释为什么使用动态HTTP代理对于提升网站的SEO效果至关重要,并分享一些实用的技巧。 首先......
  • 高效爬虫策略:利用HTTP代理实现请求合并与并发
    身为一名专业的爬虫程序员,我要跟大家分享一个超实用的技巧,就是怎么利用HTTP代理来实现高效的爬虫策略,同时实现请求合并和并发。听起来是不是就高端大气上档次?我给你们详细说说,让你们秒懂怎么搞定这玩意儿。首先,我们要理解一下为什么要用HTTP代理来实现这个高效策略。当......
  • GEWE框架-加好友请求验证
    好友请求验证小提示:v_3 v_4可以参考搜索接口请求URL:http://域名地址/api/contacts/verifyuser请求方式:POST请求头:Content-Type:application/jsonX-GEWE-TOKEN:后台获取参数:参数名必填数据类型说明appid是string设备idconfig否object其他配置config.card_nickname否string昵称c......
  • HTTP代理出现400错误的原因及解决办法
    在使用HTTP代理过程中,会经常出现各种代码错误的提示,以下是使用HTTP代理出现400代码的原因和解决办法使用HTTP代理时,出现400BadRequest错误代码通常表示客户端发送的请求格式不正确或包含了无效的参数。下面是一些可能导致400错误的原因:请求参数错误:请求中的参数格式不正确或缺少......
  • 直播源码异步处理技术:处理用户请求的挑战
    在网络技术的快速发展背景下,直播源码平台已经成为了人们社交娱乐的重要工具,直播源码平台的用户会在平台内进行观看直播、短视频,与其他用户进行交流、交谈。由于直播源码平台用户数量的逐日增加,使得直播源码平台每天要应对、处理大量的用户请求,增加了平台的压力,为了应对每天处理大......
  • 高并发数据抓取实战:使用HTTP爬虫ip提升抓取速度
    又到每天一期学习爬虫的时间了,作为一名专业的爬虫程序员,今天要跟你们分享一个超实用的技巧,就是利用HTTP爬虫ip来提升高并发数据抓取的速度。听起来有点高大上?别担心,我会用通俗易懂的话来和你们说,让你们秒懂怎么操作的。首先,咱们得理解一下为什么HTTP爬虫ip可以加速数据抓取。抓取数......