手写Openfeign实现原理——极简版

这篇具有很好参考价值的文章主要介绍了手写Openfeign实现原理——极简版。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

最近开发cloud项目,里面涉及到服务间调用,最后使用的openfeign解决的,于是对于openfeign的底层原理有些兴趣了,提前透露一下底层无非就是使用一些http调用的工具帮助我们实现了请求调用

Openfeign实现思路

手写Openfeign实现原理——极简版,java,spring,spring boot,spring cloud,openfeign

前期准备

基本依赖项

  • 首先创建一个springboot项目
  • 有一个发送请求的工具这里使用的ribbon
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.2.9.RELEASE</version>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>

开始实现

自定义注解

创建两个自定义注解,分别用于在启动类和接口上添加

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@Import(OpenInstantiationAwareBeanPostProcessor.class)//这个类重点注意
public @interface EnableRemoteClient {
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface CustomProxy {
    String servie();

    String address();
}

自定义代理类

代理类中有一个RestTemplate 用于发送请求

public class CustomProxyHandler implements InvocationHandler {
    RestTemplate restTemplate=new RestTemplate();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //首先获取对象
        Class<?> declaringClass = method.getDeclaringClass();
        //判断当前的被代理对象是否使用了自定的注解
        if(declaringClass.isAnnotationPresent(CustomProxy.class)){
            CustomProxy annotation = declaringClass.getAnnotation(CustomProxy.class);
            String servie = annotation.servie();
            String address = annotation.address();
            String url=address+servie;
            RequestMapping annotation1 = method.getAnnotation(RequestMapping.class);
            url=url+annotation1.value()[0];
            //判断请求方法是否入参
            if(args!=null&&args.length!=0){
                return restTemplate.getForObject(url,method.getReturnType(),args);
            }else {
                return restTemplate.getForObject(url,method.getReturnType());
            }
        }else{
            return null;
        }


    }
}

定义创建代理对象的工厂

用于创建代理对象使用,

public class ProxyFactory {

    /**
     * @description: 创建具体的代理对象
     * @author: 
     * @date: 2023/9/1 17:30
     * @param: [targetInterface]
     * @return: T
     **/
    public static  <T> T createProxy(Class<T> targetInterface) {
        return (T) Proxy.newProxyInstance(
                targetInterface.getClassLoader(),
                new Class[]{targetInterface},
                new CustomProxyHandler()
        );
    }
}

InstantiationAwareBeanPostProcessor实现bean的注入

InstantiationAwareBeanPostProcessor 是 Spring 框架提供的一个扩展接口,它在 Spring 容器实例化 bean 之前和之后对 bean 进行处理。其主要功能如下:

  1. 实例化前置处理(Before Instantiation):在 Spring 容器实例化 bean 之前,可以通过这个接口来自定义 bean 的实例化方式。可以在此处进行一些特殊的准备工作,比如使用自定义的实例化逻辑或者切入点的选择。

  2. 实例化后置处理(After Instantiation):在 Spring 容器实例化 bean 之后,可以通过这个接口对实例化后的 bean 进行处理。可以在此处做一些初始化的操作,比如对属性进行赋值、调用初始化方法等。

  3. 属性设置前置处理(Before Property Set):在 Spring 容器对 bean 的属性进行设置之前,可以通过这个接口来自定义属性的设置方式。可以在此处对属性进行修改或者校验操作。

  4. 属性设置后置处理(After Property Set):在 Spring 容器对 bean 的属性进行设置之后,可以通过这个接口对属性设置后的 bean 进行处理。可以在此处做一些属性设置后的额外操作。

  5. 初始化前置处理(Before Initialization):在 Spring 容器对 bean 进行初始化之前,可以通过这个接口来自定义初始化的方式。可以在此处添加一些额外的初始化逻辑。

  6. 初始化后置处理(After Initialization):在 Spring 容器对 bean 进行初始化之后,可以通过这个接口对初始化后的 bean 进行处理。可以在此处做一些初始化后的额外操作。

通过实现 InstantiationAwareBeanPostProcessor 接口,并重写其中的方法,可以在 Spring 容器实例化和初始化 bean 的各个阶段进行自定义处理,从而灵活地对 bean 进行定制化的操作。

OpenInstantiationAwareBeanPostProcessor 自定义

public class OpenInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor, ApplicationContextAware {


    private ApplicationContext applicationContext;
    /**
     * 实例化前,后面的方法可以不用看了
     * @param beanClass
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {

        log.info("正在为:{}生成代理对象,被代理的类为:{}",beanName,beanClass.getName());
        if(!beanClass.isAnnotationPresent(CustomProxy.class)){
            return null;
        }

        //动态代理里面需要实现的方法,本文采用的是jdk动态代理
        //返回代理对象
        Object object = ProxyFactory.createProxy(beanClass);
        return object;

    }

    /**
     * 实例化后
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return pvs;
    }


    /**
     * 初始化钱
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    /**
     * 初始化后
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

feign接口

这里的servie没有参数是因为我的另一个服务中没有配置context-path,如果你们自己的被调用的那个demo中有配置一定要加上哦

@CustomProxy(servie = "",address = "http://localhost:8082/")
public interface MyService {
    @RequestMapping(value = "/QuerySubselect",method = RequestMethod.GET)
    String  test();
}

启动类

@SpringBootApplication
@EnableRemoteClient
@Import({MyService.class})//接口注册,注意接口上面加@Se
public class OpenfeignDemoApplication {

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

小结

这个版本的实现目前spring还不能识别我添加了注解的这个接口,需要在启动类中使用@Import注解手动配置,所以还不完善,不过后期会继续完善的,目前博主也在学习这个,这个思路就是基于我们正常的发送请求,然后有一个人帮助我们去发送请求这个思路。

踩坑记录

@Import

将接口 MyService 标识为 @Import 的目的是为了在启动类中将该接口的实现类(动态代理对象)注册到 Spring 容器中。

当你使用 @Import({MyService.class}) 注解时,Spring 在启动时会扫描被注解的类,并根据其类型进行相应的处理。在这种情况下,MyService 类型是一个接口,而不是一个具体的类。

由于接口无法直接实例化,因此你需要在某处提供对 MyService 接口的实现类的创建逻辑。通常情况下,使用动态代理是一种常见的方式。

动态代理是一种在运行时生成代理类的机制,可以在不修改原始接口源代码的情况下,通过代理类来增强接口的功能。你可以编写一个动态代理类,实现 MyService 接口,并在动态代理类中实现你所需的增强逻辑。

然后,在启动类上使用 @Import 注解,并传入该动态代理类的类型,告诉 Spring 在启动时需要将该动态代理类注册到容器中。

这样,当其他组件需要使用 MyService 接口时,Spring 容器会从容器中获取该接口的实例,而实际上获得的是动态代理对象,该代理对象会在实际调用接口方法时根据你的增强逻辑进行处理。

  • 因为openfeign的标注的接口没有实现类所以需要这个@Import注解帮助我们告诉spring容器可以去容器中去这个类型的代理对象,目前还没有想到其他的方式不用使用@Import注解声明的。后续再想想办法

@Component和@Configuration区别是什么

@Configuration 注解不能直接添加到接口上,因为 @Configuration 注解是用于标识一个类为配置类的注解,它主要用于定义和配置 Bean 对象以及其他的配置信息。
手写Openfeign实现原理——极简版,java,spring,spring boot,spring cloud,openfeign
一开始使用这个注解导致使用了自定义注解的接口一直扫描不到,后来修改为@Component注解后就可以了。
@Component 是 Spring Framework 中的一个核心注解之一,用于将一个普通的 Java 类标识为一个可以被 Spring 自动扫描并管理的组件。

具体来说,@Component 注解可以应用于以下场景:

  1. Bean 的自动扫描和注册:通过在类上添加 @Component 注解,Spring 容器会自动扫描并将该类作为 Bean 进行注册,使得我们可以通过依赖注入等方式方便地使用该类的实例。

  2. 类型指定的自动装配:当需要进行自动装配时,Spring 容器会检测所有被 @Component 注解标记的类,并根据类型进行自动装配。

@Component 注解是一个通用的注解,如果对应的类有更具体的角色或作用,还可以使用其他派生注解,例如:

  • @Controller:标识一个类是控制器(Controller)组件,用于接收和处理用户请求。
  • @Service:标识一个类是服务(Service)组件,用于封装业务逻辑。
  • @Repository:标识一个类是数据访问仓库(Repository)组件,用于访问持久化数据。

这些派生注解都是基于 @Component 注解的,继承了 @Component 的功能,并且更明确地表示了对应类的角色和用途。

总结

对于这次去实现这么一个框架的小demo自己思考了一天,不能说很长时间吧,主要是因为对于spring中提供的一些扩展机制还不是很了解,但是我相信它一定提供了能够解决我上面问题的一些api的,只是我还没有发现而已。对于这些技术的应用底层原理还是很简单的,可以发现无非就是没让你做的事情有人帮你做了这么一个过程,只不过这个过程被人封装起来以后就是一个技术或者一个框架了。文章来源地址https://www.toymoban.com/news/detail-688762.html

到了这里,关于手写Openfeign实现原理——极简版的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何在Spring Boot中使用OpenFeign,这一篇足够了。

    第一章 如何在Spring Boot中使用OpenFeign,这一篇足够了。 第二章 OpenFeign修改默认通讯协议Https 第三章 OpenFeign默认通讯方式修改成OkHttp,包含FeignConfigruation自定义、OkHttp客户端自定义详细配置介绍 OpenFeign是一个声明式、模板化的HTTP客户端,可以帮助我们更加便捷地编写基于H

    2024年02月07日
    浏览(30)
  • Docker实践:使用Docker搭建个人开发环境(极简版)

    本文是在服务器 已经配置好 Docker 相关环境的情况下, 简要介绍 了在服务器上如何构建 Docker 镜像来搭建个人开发环境,因此本文 不涉及 Docker 的安装、维护以及各种细节,若要 详细了解 ,请查看文章最后的 相关资料 一节。 以下所有操作均在同一文件夹(例如 /media/user

    2024年02月16日
    浏览(31)
  • openGauss学习笔记-03 openGauss极简版单节点安装

    openGauss支持以脚本方式进行极简安装,极简安装包括单节点安装和一主一备节点安装。 3.1 获取安装包 3.1.1 下载对应平台的安装包 从openGauss开源社区下载对应平台的安装包 登录openGauss开源社区,选择对应平台的最新安装包下载。对于个人开发者或非企业级环境,下载极简安

    2024年02月13日
    浏览(31)
  • 如何用python连接mysql和mongodb数据库【极简版】

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【宝藏入口】。 为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章

    2024年01月23日
    浏览(36)
  • C#搭建Socket服务器与客户端,助你快速入门(极简版)

    一、服务器的搭建 新建一个C#控制台应用,用于搭建服务器。 注:serverManager除了接收消息,也可以发送消息。基本除了监听,其他活它都干。 二、客户端的搭建 再建一个C#控制台应用,用于搭建客户端。 搭建好之后,先运行服务器端, 再运行客户端。 一个简单的服务器和

    2024年02月09日
    浏览(36)
  • 个性化定制界面 VS 极简版原装界面:你更喜欢哪一个?为什么?

    不管昨天、今天、明天,能豁然开朗就是最美好的一天。 个性化定制界面和极简版原装界面,哪一个你用起来更加顺手呢,相比之下你更喜欢哪一个? 随着移动互联网技术的不断发展,手机和电脑成为我们生活中必不可少的工具之一。而手机和电脑界面作为我们与手机交互

    2024年02月11日
    浏览(42)
  • 基于react18+vite4+arco.design搭建极简版后台管理模板

    趁着国庆前夕整了一个 vite4 结合 react18 搭建后台管理模板,搭配上字节团队react组件库 ArcoDesign ,整体操作功能非常丝滑。目前功能 支持多种模板布局、暗黑/亮色模式、国际化、权限验证、多级路由菜单、tabview标签栏快捷菜单、全屏控制 等功能。极简非凡的布局界面、高定

    2024年02月08日
    浏览(37)
  • 【Spring Boot】SpringBoot参数验证以及实现原理

    参数验证很重要,是平时开发环节中不可少的一部分,但是我想很多后端同事会偷懒,干脆不做,这样很可能给系统的稳定性和安全性带来严重的危害。 那么在Spring Boot应用中如何做好参数校验工作呢,本文提供了小技巧以及验证实现原理,你知道几个呢? Spring Boot 提供了内

    2023年04月16日
    浏览(44)
  • 用Spring Boot轻松实现定时任务--原理详解

      在现代化的web开发中,定时任务是一个非常常见的功能。Spring Boot为我们提供了一个简便的方式来处理这些任务,我们只需加入一些注解和配置即可完成。本文将介绍 Spring Boot 定时任务的基本概念和原理,以及如何在具体业务场景中使用和优化配置。   定时任务是指在

    2024年02月06日
    浏览(40)
  • Spring Boot源码解读与原理剖析:深入探索Java开发的奥秘!

    关注+点赞+评论,评论区回复“Spring Boot源码解读与原理剖析:深入探索Java开发的奥秘!” 每篇最多 评论3条 !!采用抽奖助手自动拉取评论区有效评论送书两本, 开奖时间:9月11号 承载着作者的厚望,掘金爆火小册同名读物 《Spring Boot源码解读与原理剖析》 正式出书!

    2024年02月10日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包