SpringCloud:服务调用——自定义Feign方式实现

2024-06-21 02:48

本文主要是介绍SpringCloud:服务调用——自定义Feign方式实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一,SpringCloud 服务调用

    Spring Cloud提供了两种方式进行服务调用,分别为RestTemplate方式和声明式Feign方式,两种代用方式在 SpringCloud:注册中心——Eureka中已经进行了演示;本篇文章参考Feign方式,整合RestTemplate实现自定义的声明式Feign调用

二,步骤解析

    1,搭建简易声明式Feign客户端调动环境,并初步调通

    2,重写@EnableFeignClients -> @EnableRestFeign,引用注册类为下面自定义注册类

    3,重写@FeignClient -> @RestClient

    4,重写注册类 -> RestFeignRegisters,通过JDK动态代理获取被@RestClient注解接口的代理对象,并通过BeanDeifinition注册到Spring Bean容器

    5,重写JDK动态代理需要的InvocationHandler方法 -> MyInvocationHandler,拼接访问URL后,通过RestTemplate进行调用

三,调用流程

    1,启动服务时:

        @EnableRestFeign -> RestFeignRegisters :注册被@RestClient注解的接口代理对象到SpringBean容器中

    2,服务调用时:

        * 通过调用的接口路径@RequestMapping及参数@RequestParam拼接服务访问路径 http://{serviceName}/{uri}?{param}

        * 拼接成功后,通过RestTemplate直接进行服务调用

四,服务端代码

    * 接口 -> com-gupao-springcloud-selffeign-server-api

package com.gupao.self.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;/*** @author pj_zhang* @create 2019-01-24 0:38**/
public interface ISelfFeignController {@RequestMapping("/getMessage")String getMessage(@RequestParam("message") String message);
}

    * 实现类 -> com-guapo-springcloud-selffeign-server

package com.gupao.self.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @author pj_zhang* @create 2019-01-24 0:30**/
@RestController
public class SelfFeignController implements ISelfFeignController {@Override@RequestMapping("/getMessage")public String getMessage(@RequestParam("message") String message) {return "SelfFeignController.getMessage : " + message;}}

五,代码实现

    1,搭建简易声明式Feign客户端调动环境,并初步调通

        * 项目架构,具体方式参考博文SpringCloud:注册中心——Eureka

        * 客户端自定义Feign结构

    2,重写@EnableFeignClients -> @EnableRestFeign

package com.gupao.self.feign.annotation;import com.gupao.self.feign.register.RestFeignRegisters;
import org.springframework.context.annotation.Import;import java.lang.annotation.*;/*** @author pj_zhang* @create 2019-01-23 23:01**/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({RestFeignRegisters.class})
public @interface EnableRestFeign {/*** 指定RestClient端口* @return*/Class<?>[] clients() default {};}

    3,重写@FeignClient -> @RestClient

package com.gupao.self.feign.annotation;import java.lang.annotation.*;/*** @author pj_zhang* @create 2019-01-23 23:08**/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RestClient {String name() default "";
}

    4,重写注册类 -> RestFeignRegisters,通过JDK动态代理获取被@RestClient注解接口的代理对象,并通过BeanDeifinition注册到Spring Bean容器

package com.gupao.self.feign.register;import com.gupao.self.feign.annotation.EnableRestFeign;
import com.gupao.self.feign.annotation.RestClient;
import com.gupao.self.feign.handler.MyInvocationHandler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.stream.Stream;/*** @author pj_zhang* @create 2019-01-23 23:01**/
public class RestFeignRegisters implements ImportBeanDefinitionRegistrar, BeanFactoryAware {private BeanFactory beanFactory;@Overridepublic void registerBeanDefinitions(AnnotationMetadata annotationMetadata,BeanDefinitionRegistry registry) {// 获取@EnableRestFeign模块引用的需要注册的Class类Map<String, Object> attributes =annotationMetadata.getAnnotationAttributes(EnableRestFeign.class.getName());Class<?>[] classes = (Class<?>[]) attributes.get("clients");// 筛选所有被Feign客户端注解的类, @RestFeignStream.of(classes)// 过滤接口.filter(Class::isInterface)// 仅选择标注了@RestClient注解的接口.filter(interfaceClass ->AnnotationUtils.findAnnotation(interfaceClass, RestClient.class) != null)// 过滤requestMapping方法.forEach(restClientClass -> {// 通过@RestClient源数据获取应用名称RestClient restClient =AnnotationUtils.findAnnotation(restClientClass, RestClient.class);String serverName = restClient.name();// @RestClient注解类进行动态代理Object proxy = Proxy.newProxyInstance(registry.getClass().getClassLoader(),new Class[]{restClientClass},new MyInvocationHandler(serverName, beanFactory));// 将@RestClient接口代理实现proxy注册为BeanString beanName = "RestClient." + serverName;// 通过SingletonBeanRegistry注册bean
//                    if (registry instanceof SingletonBeanRegistry) {
//                        SingletonBeanRegistry singletonBeanRegistry = (SingletonBeanRegistry) registry;
//                        singletonBeanRegistry.registerSingleton(serverName, proxy);
//                    }// 通过BeanDefinition注册BeanBeanDefinitionBuilder beanDefinitionBuilder =BeanDefinitionBuilder.genericBeanDefinition(RestClientClassFactoryBean.class);beanDefinitionBuilder.addConstructorArgValue(restClientClass);beanDefinitionBuilder.addConstructorArgValue(proxy);BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();registry.registerBeanDefinition(beanName, beanDefinition);});}/*** 自定义FactoryBean类,* 通过BeanDefinition方式注册类到Spring Bean容器中*/private static class RestClientClassFactoryBean implements FactoryBean {private final Class<?> restClient;private final Object proxy;private RestClientClassFactoryBean(Class<?> restClient, Object proxy) {this.restClient = restClient;this.proxy = proxy;}@Overridepublic Object getObject() throws Exception {return proxy;}@Overridepublic Class<?> getObjectType() {return restClient;}}/*** 通过集成BeanFactoryWare获取Spring Bean容器* 在通过RestTemplate进行方法调用时, 获取RestTemplate* @param beanFactory* @throws BeansException*/@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}
}

    5,重写JDK动态代理需要的InvocationHandler方法 -> MyInvocationHandler,拼接访问URL后,通过RestTemplate进行调用

package com.gupao.self.feign.handler;import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;/*** @author pj_zhang* @create 2019-01-23 23:26**/
public class MyInvocationHandler implements InvocationHandler {// ServiceNameprivate final String serviceName;private final BeanFactory beanFactory;public MyInvocationHandler(String serviceName, BeanFactory beanFactory) {this.serviceName = serviceName;this.beanFactory = beanFactory;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 通过RestTemplate进行调用, 进行拼接访问路径, 访问路径参数如下// http://servierName/uri?param// 此处需要获取三个参数, 分别为// serviceName 从register获取// url 从方法注解获取RequestMapping requestMapping = AnnotationUtils.getAnnotation(method, RequestMapping.class);String[] uri = requestMapping.value();// param// 获取方法参数数量int count = method.getParameterCount();// 获取方法参数类型集合Class<?>[] parameterTypes = method.getParameterTypes();StringBuilder sb = new StringBuilder();for (int i = 0; i < count; i ++) {// 获取方法第i个参数注解Annotation[] annotations = method.getParameterAnnotations()[i];// 遍历注解, 获取参数名称String paramName = null;for (Annotation annotation : annotations) {if (annotation instanceof RequestParam) {paramName = ((RequestParam) annotation).value();break;}}String paramValue = String.valueOf(args[i]);// 拼接参数sb.append("&").append(paramName).append("=").append(paramValue);}// 拼接完整URL// http://servierName/uri?paramStringBuilder acturalUrl = new StringBuilder();acturalUrl.append("http://").append(serviceName).append("/").append(uri[0]).append("?").append(sb.toString());// 从BeanFactory容器中获取RestTemplate, 进行执行RestTemplate restTemplate = beanFactory.getBean("restTemplate", RestTemplate.class);return restTemplate.getForObject(acturalUrl.toString(), String.class);}
}

    6,feign接口 -> 注意@FeignClient注解替换为@RestFeign

package com.gupao.self.feign;import com.gupao.self.controller.ISelfFeignController;
import com.gupao.self.feign.annotation.RestClient;
import org.springframework.cloud.openfeign.FeignClient;/*** @author pj_zhang* @create 2019-01-24 0:42**/
@RestClient(name = "server-feign")
public interface ISelfFeign extends ISelfFeignController {
}

    7,启动类 -> 注意@EnableFeignClients替换为@EnableRestFeign

package com.gupao;import com.gupao.self.feign.ISelfFeign;
import com.gupao.self.feign.annotation.EnableRestFeign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;/*** @author pj_zhang* @create 2019-01-24 0:34**/
@SpringBootApplication
//@EnableFeignClients
@EnableRestFeign(clients = ISelfFeign.class)
@EnableEurekaClient
public class SelfFeignClientApp {public static void main(String[] args) {SpringApplication.run(SelfFeignClientApp.class, args);}@LoadBalanced@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}}

    8,调用结果

这篇关于SpringCloud:服务调用——自定义Feign方式实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1080003

相关文章

Java如何根据word模板导出数据

《Java如何根据word模板导出数据》这篇文章主要为大家详细介绍了Java如何实现根据word模板导出数据,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... pom.XML文件导入依赖 <dependency> <groupId>cn.afterturn</groupId>

利用Python实现可回滚方案的示例代码

《利用Python实现可回滚方案的示例代码》很多项目翻车不是因为不会做,而是走错了方向却没法回头,技术选型失败的风险我们都清楚,但真正能提前规划“回滚方案”的人不多,本文从实际项目出发,教你如何用Py... 目录描述题解答案(核心思路)题解代码分析第一步:抽象缓存接口第二步:实现两个版本第三步:根据 Fea

Java应用如何防止恶意文件上传

《Java应用如何防止恶意文件上传》恶意文件上传可能导致服务器被入侵,数据泄露甚至服务瘫痪,因此我们必须采取全面且有效的防范措施来保护Java应用的安全,下面我们就来看看具体的实现方法吧... 目录恶意文件上传的潜在风险常见的恶意文件上传手段防范恶意文件上传的关键策略严格验证文件类型检查文件内容控制文件存储

Go语言使用slices包轻松实现排序功能

《Go语言使用slices包轻松实现排序功能》在Go语言开发中,对数据进行排序是常见的需求,Go1.18版本引入的slices包提供了简洁高效的排序解决方案,支持内置类型和用户自定义类型的排序操作,本... 目录一、内置类型排序:字符串与整数的应用1. 字符串切片排序2. 整数切片排序二、检查切片排序状态:

浅析Java如何保护敏感数据

《浅析Java如何保护敏感数据》在当今数字化时代,数据安全成为了软件开发中至关重要的课题,本文将深入探讨Java安全领域,聚焦于敏感数据保护的策略与实践,感兴趣的小伙伴可以了解下... 目录一、Java 安全的重要性二、敏感数据加密技术(一)对称加密(二)非对称加密三、敏感数据的访问控制(一)基于角色的访问

python利用backoff实现异常自动重试详解

《python利用backoff实现异常自动重试详解》backoff是一个用于实现重试机制的Python库,通过指数退避或其他策略自动重试失败的操作,下面小编就来和大家详细讲讲如何利用backoff实... 目录1. backoff 库简介2. on_exception 装饰器的原理2.1 核心逻辑2.2

Java计算经纬度距离的示例代码

《Java计算经纬度距离的示例代码》在Java中计算两个经纬度之间的距离,可以使用多种方法(代码示例均返回米为单位),文中整理了常用的5种方法,感兴趣的小伙伴可以了解一下... 目录1. Haversine公式(中等精度,推荐通用场景)2. 球面余弦定理(简单但精度较低)3. Vincenty公式(高精度,

使用Java将实体类转换为JSON并输出到控制台的完整过程

《使用Java将实体类转换为JSON并输出到控制台的完整过程》在软件开发的过程中,Java是一种广泛使用的编程语言,而在众多应用中,数据的传输和存储经常需要使用JSON格式,用Java将实体类转换为J... 在软件开发的过程中,Java是一种广泛使用的编程语言,而在众多应用中,数据的传输和存储经常需要使用j

Java实现视频格式转换的完整指南

《Java实现视频格式转换的完整指南》在Java中实现视频格式的转换,通常需要借助第三方工具或库,因为视频的编解码操作复杂且性能需求较高,以下是实现视频格式转换的常用方法和步骤,需要的朋友可以参考下... 目录核心思路方法一:通过调用 FFmpeg 命令步骤示例代码说明优点方法二:使用 Jaffree(FF

基于C#实现MQTT通信实战

《基于C#实现MQTT通信实战》MQTT消息队列遥测传输,在物联网领域应用的很广泛,它是基于Publish/Subscribe模式,具有简单易用,支持QoS,传输效率高的特点,下面我们就来看看C#实现... 目录1、连接主机2、订阅消息3、发布消息MQTT(Message Queueing Telemetr