手写openFeign

news2024/11/17 1:27:25

目录

  • 背景
  • 步骤
    • 应用级别:
      • 1、建立被调用方
      • 2、建立调用方
        • 引入依赖
      • 3、实现效果
    • 原理级别:
      • 调动方代码(注意impl在启动的时候里边是空的)
      • 引入jar包的代码(写好以后推到nexus上,再拉到上述调用方服务中)
  • 升华

背景

最近在手写各种组件,以便于更加了解组件的原理为我所用。现在写到openFeign,让我们一起来看一看是怎么用的吧。
了解基本概念看这里:OpenFeign官网相关知识
OpenFeign是一个用于构建基于Java的RESTful客户端的开源框架。它提供了一种简单的、声明性的方式来定义和使用HTTP请求,同时也有许多好处,包括:

简化HTTP请求:OpenFeign通过提供注解和接口的方式,使得定义HTTP请求变得非常简单明了。你只需要定义一个接口,然后在接口的方法上添加注解来描述请求参数、请求路径等信息,即可完成HTTP请求的构建。

减少样板代码:传统的HTTP请求通常涉及大量的样板代码,如创建HTTP连接、处理请求参数、处理响应等。OpenFeign自动处理这些细节,使得你的代码更加简洁,专注于业务逻辑。

内置负载均衡:OpenFeign与Spring Cloud等微服务框架集成时,可以使用Ribbon作为负载均衡器。这使得你可以在多个服务实例之间分发请求,提高系统的可伸缩性和容错性。

支持插件扩展:OpenFeign提供了许多扩展点,可以通过编写插件来增强或修改其默认行为,满足特定需求。

整合服务发现:OpenFeign可以与Eureka等服务发现组件集成,使得你可以通过服务名来访问其他微服务,而无需硬编码服务的具体地址。

与Spring Cloud集成:OpenFeign是Spring Cloud的一部分,因此与Spring Cloud的其他组件(如Eureka、Hystrix等)无缝集成,形成完整的微服务生态系统。

支持异步请求:OpenFeign可以通过@Async注解支持异步请求,使得你可以更高效地处理并发请求。

总的来说,OpenFeign简化了基于RESTful的HTTP请求的创建和使用过程,帮助你构建更加简洁、可维护、可扩展的微服务应用。同时,它的集成能力和插件扩展性使得它成为构建复杂分布式系统的有力工具。

步骤

应用级别:

1、建立被调用方

在这里插入图片描述

package com.example.openfeign.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/demo")
public class FeignDemoController {


    @GetMapping("/test")
    public String test(){
        return "hello openfegin";
    }

}


server:
  port: 8081

2、建立调用方

在这里插入图片描述

引入依赖

    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
package com.example.openfeign1.controller;

import com.example.openfeign1.FeginDemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/feginDemo")
public class FeginDemoController {

    @Autowired
    FeginDemo feginDemo;


    @GetMapping("/test")
    public String test(){
        String test = feginDemo.test();
        return test;
    }

}


package com.example.openfeign1;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "demo",url = "http://localhost:8081/demo")
public interface FeginDemo {


    @GetMapping("test") //被调用接口的请求类型
    String test();

}


3、实现效果

在这里插入图片描述

原理级别:

调动方代码(注意impl在启动的时候里边是空的)

引入自定义jar包

   <dependency>
            <groupId>com.example</groupId>
            <artifactId>SDK</artifactId>
            <version>0.0.1-20230729.134847-9</version>
        </dependency>

在这里插入图片描述

package com.example.sdk.positive.Annotation;



import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


@Retention(RetentionPolicy.RUNTIME) // 指定注解保留时期,这里设置为运行时可见
public @interface MyAnnotationFeign {
    // 定义属性
    String name(); // 定义一个名为 "name" 的属性,没有默认值,使用时需要指定值
    String url();  // 定义一个名为 "url" 的属性,没有默认值,使用时需要指定值
}

package com.example.sdk.positive.controller;


import com.example.sdk.positive.Interface.RemoteService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/feginDemo")
public class FeginDemoController  {
    
     static    RemoteService remoteService;


//    public static void main(String[] args) throws Exception {
//        FeignClientProxy feignClientProxy = new FeignClientProxy();
//        feignClientProxy.proxy();
//        String test = remoteService.test();
        return test;
//    }

    @GetMapping("/test1")
    public String test1() throws Exception {

        String test = remoteService.test();
        return test;
    }

    


import com.example.sdk.positive.Annotation.MyAnnotationFeign;
import org.springframework.web.bind.annotation.GetMapping;

@MyAnnotationFeign(name = "demo",url = "http://localhost:8080")
public interface RemoteService {
    @GetMapping("/test")
    String test() throws NoSuchMethodException;
}


@SpringBootApplication
public class OpenFeignByHandApplication   {

    public static void main(String[] args) {
        SpringApplication.run(OpenFeignByHandApplication.class, args);
    }



}
package com.example.positive.Impl;
import com.example.positive.Annotation.MyAnnotationFeign;
import com.example.positive.Interface.${ClassName};
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.client.RestTemplate;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;



public class ${ClassName}Impl   implements ${ClassName} {

public ${ClassName}Impl() {

}

@Override
public String ${MethodName}() throws NoSuchMethodException {


Class<${ClassName}> clazz =${ClassName}.class;
    Annotation[] annotations = clazz.getAnnotations();

    String url = null;
    String methodUrl = null;

    for (Annotation annotation : annotations) {
    if (annotation instanceof MyAnnotationFeign) {
    MyAnnotationFeign myClassAnnotation = (MyAnnotationFeign) annotation;
    url = myClassAnnotation.url();
    }
    }

    Method method = RemoteService.class.getMethod("${MethodName}");


    GetMapping getMappingAnnotation = method.getAnnotation(GetMapping.class);
    if (getMappingAnnotation != null) {
    methodUrl = getMappingAnnotation.value()[0];
    } else {
    System.out.println("GetMapping annotation not found.");
    }

    String baseUrl = url+methodUrl;
    RestTemplate restTemplate = new RestTemplate();
    String result = restTemplate.getForObject(baseUrl, String.class);
    System.out.println(result);
    return result;
    }
    }

引入jar包的代码(写好以后推到nexus上,再拉到上述调用方服务中)

在这里插入图片描述

package com.example.sdk.sdk.controller;


import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

public class Compiler extends ClassLoader {
    public void compiler(String compilerPath,String javaPath){

        JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
        int status = javac.run(null, null, null, "-d",
                compilerPath,javaPath);
        if(status!=0){
            System.out.println("没有编译成功!");
        }
    }
}
package com.example.sdk.sdk.controller;

//import com.example.proxy1.Compiler;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;




public class FeignClientProxy   {

    public void proxy() throws Exception {
        //遍历包下的接口,获取接口的名称还有方法名
        String packageName = "com.example.positive.Interface";
        List<Class<?>> interfaces = getInterfacesInPackage(packageName);

        String interfaceName;
        String methodName = null;
        for (Class<?> interfaceClass : interfaces) {
            interfaceName = interfaceClass.getSimpleName();
            System.out.println("Interface Name: " + interfaceName);
            Method[] methods = interfaceClass.getDeclaredMethods();
            for (Method method : methods) {
                methodName = method.getName();
                System.out.println("  Method Name: " + methodName);
            }

            String path = "C:\\Users\\Administrator\\Desktop\\OpenFeignByHand\\src\\main\\resources";
            VelocityEngine velocityEngine = new VelocityEngine();
            // 配置资源加载器为FileResourceLoader
            velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
            velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, path);
            velocityEngine.init();
            Template template = velocityEngine.getTemplate("ImplTemplate.vm");

            // 创建Velocity上下文
            VelocityContext context = new VelocityContext();
            // 设置变量值
            context.put("MethodName", methodName);
            context.put("ClassName", interfaceName);

            // 将模板解析为字符串
            StringWriter stringWriter = new StringWriter();
            template.merge(context, stringWriter);

            String javaPath = "C:\\Users\\Administrator\\Desktop\\OpenFeignByHand\\src\\main\\java\\com\\example\\positive\\Impl\\";
            //替换后输出的文件位置
            PrintWriter printWriter = new PrintWriter(javaPath + interfaceName + "Impl.java");
            printWriter.write(stringWriter.toString().toCharArray());
            printWriter.flush();
            printWriter.close();

            String path1 = javaPath + interfaceName + "Impl.java";
            Compiler compiler = new Compiler();
            compiler.compiler(System.getProperty("user.dir")+"\\target\\classes",path1);
            Class<?> implClass = Class.forName("com.example.positive.Impl."+interfaceName+"Impl");
            //将controller中的属性值注入进去,扫描到当前接口,将实现类注入进去,怎么办?
            //
//            Class<?> feginDemoControllerClass = FeginDemoController.class;

            String packageName1 = "com.example.positive.controller";
            List<Class<?>> interfaces1 = getInterfacesInPackage(packageName1);
            Class<?> feginDemoControllerClass = interfaces1.get(0);
            String result = firstLetterToLower(interfaceName);
            Field nameField = feginDemoControllerClass.getDeclaredField(result);
            nameField.setAccessible(true);
            Object instance = implClass.newInstance();
            nameField.set(feginDemoControllerClass, instance);

        }


    }

    public static String firstLetterToLower(String input) {
        if (input == null || input.isEmpty()) {
            return input;
        }
        return input.substring(0, 1).toLowerCase() + input.substring(1);
    }

    private static List<Class<?>> getInterfacesInPackage(String packageName) {
        List<Class<?>> interfaces = new ArrayList<>();

        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            String path = packageName.replace('.', '/');
            java.net.URL resource = classLoader.getResource(path);

            if (resource != null) {
                java.io.File folder = new java.io.File(resource.getFile());

                if (folder.exists() && folder.isDirectory()) {
                    for (java.io.File file : folder.listFiles()) {
                        String fileName = file.getName();
                        if (fileName.endsWith(".class")) {
                            String className = packageName + '.' + fileName.substring(0, fileName.length() - 6);
                            Class<?> clazz = Class.forName(className);
//                            if (clazz.isInterface()) {
                                interfaces.add(clazz);
//                            }
                        }
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return interfaces;
    }
}


package com.example.sdk.sdk.controller;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;



@Component
public class Test  implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        FeignClientProxy feignClientProxy = new FeignClientProxy();
        feignClientProxy.proxy();
    }
}

被调用方和应用级别的被调用方一样。

@SpringBootApplication
@RestController
public class NegativeApplication {

    public static void main(String[] args) {
        SpringApplication.run(NegativeApplication.class, args);
    }

    @GetMapping("/test")
    public String test() {
        return "Hello from Provider!";
    }

}

最后展示效果是和上边一样的。
原理很简单,先生成实现类,实现类里边有方法根据接口上边的url地址进行拼接,使用restTemplate 调用(controller中调用的时候使用实现类对象触发这个方法),就是这么简单。

升华

组件没有这么难,连接底层原理都很简单。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/814597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

收集用户隐私行为规范与修改指引

为更好地保护用户隐私信息&#xff0c;优化用户体验&#xff0c;平台对小程序内的收集用户隐私行为进行规范&#xff0c;开发者可自查所涉及的违规内容&#xff0c;并参照修改指引进行相应整改。 常见违规内容 一、隐私政策协议默示同意 小程序在收集用户数据前&#xff0c;…

【高级程序设计语言C++】二叉搜索树

1. 二叉搜索树的概念2. 二叉搜索树的功能2.1. 二叉搜索树的简单模型2.2. 二叉搜索树的查找2.3. 二叉搜索树的插入2.4. 二叉搜索树的删除 3. 二叉搜索树的性能分析 1. 二叉搜索树的概念 二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称BST&#xff09;是一种常见的二…

【C/C++】类之间的纵向关系——继承的概念

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

C++——继承(2)详解

目录 一.子类和父类对象的赋值转换 子类对象赋值父类对象的另外两种方式&#xff1a; 总结&#xff1a; 二.父类与子类的作用域 1. 在继承体系中基类和派生类都有独立的作用域。 例&#xff1a; 2.作用域练习 练习1&#xff1a; 解决方法: 一.子类和父类对象的赋值转换 …

深入理解Java类加载机制中的双亲委派模型--根据源码探讨

前言&#xff1a; 今天和大家探讨一道Java中经典的面试题&#xff0c;这道面试题经常出现在各个公司的面试中&#xff0c;本篇文章主要讲解ava类加载机制中的双亲委派模型的知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大…

Cesium态势标绘专题-三角旗标、矩形旗标、曲线旗标(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

前端JavaScript作用域详解

目录 前言 什么是作用域 作用域类型 全局作用域 局部作用域 块级作用域 ES6之前 ES6以后 作用域链 变量提升 基础概念 优先级问题 闭包 定义 特点 使用场景 封装私有变量 延长变量周期 模块化、命名空间 缓存 ES6的作用域 const、let 块级作用域 变量提…

tinymce4/5实现将word中内容(文字图片等)直接粘贴至编辑器中——利用插件tinymce-powerpaste-plugin

TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有&#xff1a;UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。 TinyMCE的优势&#xff1a; 开源可商用&#xff0c;基于LGPL2.1 插件丰富&#xff0c;自带插件基本涵盖日常…

关于时序图

时序图 01 什么是时序图&#xff1f;02 时序图的组成元素2.1 对象2.2 生命线2.3 消息 03 如何绘制 01 什么是时序图&#xff1f; 时序图是UML交互图中的一类&#xff0c;又名序列图、顺序图。 用于描述对象之间的传递消息的时间顺序&#xff08;包括发送消息、接收消息、处理…

一个女程序员的成长之路

2013年大学毕业了&#xff0c;带着迷茫与好玩&#xff0c;我还年轻的心态&#xff0c;开始在郑州寻觅工作机会&#xff0c;最后很荣幸的在一家小公司入职了&#xff0c;工作的内容是给种植大棚的用户打电话&#xff0c;推销农药。每天就是在网上各种农业平台上面找号码&#xf…

ASIC-WORLD Verilog(11)过程时序控制

写在前面 在自己准备写一些简单的verilog教程之前&#xff0c;参考了许多资料----Asic-World网站的这套verilog教程即是其一。这套教程写得极好&#xff0c;奈何没有中文&#xff0c;在下只好斗胆翻译过来&#xff08;加了自己的理解&#xff09;分享给大家。 这是网站原文&…

【vue】vue中Mixins的用法(jeecg-boot为例):

文章目录 一、jeecg-boot本身只有JeecgListMixin.js二、使用Mixin:三、mixins详解&#xff1a;【1】由于每个项目的接口和参数不同>这里引进js进行处理&#xff0c;不在Mixin里面处理了&#xff08;Mixin只做公共数据处理&#xff09;【2】公共的页面字典【3】解决方法里面不…

2009年上半年 软件设计师 上午试卷3

●下图属于UML 中的&#xff08;46),其中&#xff0c;AccountManagement 需要&#xff08;47)。 (46)A.组件图 B.部署图 C.类图 D.对象图 (47)A.实现 IdentityVerifier 接口并被 CreditCardServices 调用 B.调用 CreditCardServices 实现的 Identity Verifier 接口 C.实现 I…

设计模式大白话——装饰者模式

装饰者模式 文章目录 装饰者模式一、概述二、应用场景三、代码示例四、小结 一、概述 ​ 装饰者模式&#xff0c;此模式最核心之处在于装饰二字&#xff0c;之所以需要装饰&#xff0c;是因为基础的功能无法满足需求&#xff0c;并且装饰是临时的&#xff0c;并不是永久的&…

基于Java+spring+springMvc+mybatis+jsp学生选课管理系统

基于JavaspringspringMvcmybatisjsp学生选课管理系统 一、系统介绍二、功能展示1.课程列表(学生)2.已选课程(学生)3.已修课程(学生)4.我的课程&#xff08;老师&#xff09;5.课程打分&#xff08;老师&#xff09;6.课程管理、学生管理、教师管理&#xff08;系统管理员&#…

python字典:怎么取出key对应的值

目录 python中的字典是什么 怎么判断key是否在字典中 怎么取出key对应的值 总结 python中的字典是什么 在Python中&#xff0c;字典&#xff08;Dictionary&#xff09;是一种无序且可变的数据类型&#xff0c;用于存储键-值&#xff08;Key-Value&#xff09;对。字典通过…

电脑卡顿反应慢怎么处理?提升反应速度的方法

电脑卡顿反应慢是很常见的问题&#xff0c;然而&#xff0c;我们可以采取一些方法来处理这个问题&#xff0c;帮助大家提升电脑反应速度。​ 一、提升电脑反应速度的方法 当电脑运行顺畅时&#xff0c;我们的工作体验也会更加愉悦。然而&#xff0c;如果电脑出现卡顿反应慢的…

【项目设计】MySQL 连接池的设计

目录 &#x1f449;关键技术点&#x1f448;&#x1f449;项目背景&#x1f448;&#x1f449;连接池功能点介绍&#x1f448;&#x1f449;MySQL Server 参数介绍&#x1f448;&#x1f449;功能实现设计&#x1f448;&#x1f449;开发平台选型&#x1f448;&#x1f449;MyS…

【雕爷学编程】MicroPython动手做(24)——掌控板之拓展掌控宝

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

【雕爷学编程】MicroPython动手做(23)——掌控板之WiFi与蓝牙2

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…