我是靠谱客的博主 安详高跟鞋,最近开发中收集的这篇文章主要介绍在SpringBoot项目中封装OpenFeign写在最前封装方式,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

写在最前

由于一个单体项目SpringBoot需要对另一个单体SpingBoot进行远程调用,调用的接口比较多,且使用SpringCloud又没有必要,所以我们对OpenFeign进行了封装。(springcloud OpenFeign不好引入项目中,会有一堆依赖问题);写着玩的,简单记录一下(其实就是模仿SpringCloudOpenFeign),如有不对请指正。(有个开源的框架也是远程调用也符合这种业务场景,但是名字给忘了)

封装方式

我们将封装成一个依赖形式,方便项目使用;

依赖结构

文件结构

创建module

首先新建一个maven module ,名字如:gulimall-spring-boot-starter-feign;在pom.xml引入以下依赖:

    <properties>
        <openfegin.version>12.0</openfegin.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.gx</groupId>
            <artifactId>gulimall-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
            <version>${openfegin.version}</version>
        </dependency>

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-jackson</artifactId>
            <version>${openfegin.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

创建包

  • feign.config
    • FeignRequestInterceptor:配置远程调用http的header内容,比如传固定的token
    • IotRemoteProperties: 在yaml文件配置的远程调用配置信息
    • OpenFeignConfig: 使用open-feign固定配置
    • RemoteConfig:配置实体
  • feign.core.annotation
    • EnableFeignClients:在启动类上,包扫描注解
    • GulimallFeign:在每个接口类加的注解,表示为调用的接口,
  • feign.core.factory
    • FeignClientFactoryBean:将加了GulimallFeign注入到spring容器中
    • FeignClientRegistrar:扫描远程调用的包下面的类

具体代码

FeignRequestInterceptor

import cn.hutool.core.util.StrUtil;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.http.HttpHeaders;

/**
 *
 * 对所有远程调用的请求设置header 业务代码
 */
public class FeignRequestInterceptor implements RequestInterceptor {
    private String authorization;
    private RemoteConfig remoteConfig;

    public FeignRequestInterceptor() {
    }

    public FeignRequestInterceptor(RemoteConfig remoteConfig) {
        this.remoteConfig = remoteConfig;
    }

    @Override
    public void apply(RequestTemplate template) {
        template.header(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8;");
        if (StrUtil.isNotBlank(remoteConfig.getAuthorization())) {
            template.header(HttpHeaders.AUTHORIZATION, remoteConfig.getAuthorization());
        }
    }

}

IotRemoteProperties

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import java.util.Map;
/**
 * @date 2022/11/18
 * description: 业务配置类 配置远程调用地址 以及token等信息
 */
@Data
@Validated
@ConfigurationProperties("gulimall.remote")
@Component
public class IotRemoteProperties {
    private Map<String, RemoteConfig> remoteConfigs;
}

OpenFeignConfig

import feign.Contract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenFeignConfig {
    @Bean
    public Contract useFeignAnnotations() {
        return new Contract.Default();
    }
}

EnableFeignClients

import com.gx.framework.feign.core.factory.FeignClientRegistrar;
import org.springframework.context.annotation.Import;

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

/**
 * @version 1.0
 * @description 启动类注解 说明需要对哪些包进行扫描  如:@EnableInduschainFeignClients("com.gx.module.remote.api.*")
 * @date 2023/1/29 22:14
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(FeignClientRegistrar.class)
public @interface EnableFeignClients {
}

GulimallFeign

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @version 1.0
 * @description 每个远程调用接口类注解
 * @date 2023/1/29 22:15
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface GulimallFeign {
    /**
     * 远程调用服务后端服务名 如iot,aiot...
     * @return
     */
    String remoteName();

    String desc() default "";
}

//@InduschainFeign(remoteName = "iot", desc = "调用iot的设备接口")
//public interface IotDeviceFeignApi {
//    /**
//     * 获取设备现在信息
//     *
//     * @param serialNumbers
//     * @return
//     */
//    @RequestLine("POST /iot/device/getDeviceInfoBySerialNumbers")
//    IotAjaxResultDTO<List<IotDeviceDTO>> getIotDeviceCurrentInfo(List<String> serialNumbers);
//
//    /**
//     * 获取设备序列号
//     * @return
//     */
//    @RequestLine("GET /iot/device/generator")
//    IotAjaxResultDTO<String> getGenerator();
//
//    @RequestLine("PUT /iot/device/rule/updateAlarmRuleState/{id}/{state}")
//    IotAjaxResultDTO updateState(@Param("id") Long id, @Param("state") String state);

//
//}

FeignClientFactoryBean

import com.gx.framework.feign.config.FeignRequestInterceptor;
import com.gx.framework.feign.config.IotRemoteProperties;
import com.gx.framework.feign.config.RemoteConfig;
import com.gx.framework.feign.core.annotation.GulimallFeign;
import feign.Feign;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.Map;

/**
 * @version 1.0
 * @description 注册每一个加了GulimallFeign 塞入bean容器中
 * @date 2023/1/29 22:16
 */
public class FeignClientFactoryBean implements FactoryBean<Object>, ApplicationContextAware {
    // 目标对象(FeignClient接口)
    private Class<?> type;
    // 服务名称
    private String name;
    private ApplicationContext applicationContext;


    @Override
    public Object getObject() throws Exception {
        IotRemoteProperties iotRemoteProperties = this.applicationContext.getBean(IotRemoteProperties.class);
        Map<String, RemoteConfig> remoteConfigs = iotRemoteProperties.getRemoteConfigs();
        GulimallFeign annotation = type.getAnnotation(GulimallFeign.class);
        RemoteConfig remoteConfig = remoteConfigs.get(annotation.remoteName());
        Object feignClient = Feign.builder().decoder(new JacksonDecoder()).encoder(new JacksonEncoder())
                .requestInterceptor(new FeignRequestInterceptor(remoteConfig))
                .target(type, remoteConfig.getUrl());
        return feignClient;
    }


    @Override
    public Class<?> getObjectType() {
        return this.type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    public void setName(String name) {
        this.name = name;
    }

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

FeignClientRegistrar


import com.gx.framework.feign.core.annotation.EnableFeignClients;
import com.gx.framework.feign.core.annotation.GulimallFeign;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.util.Map;
import java.util.Set;

/**
 * @version 1.0
 * @description
 * @date 2023/1/29 22:17
 */
public class FeignClientRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    private ResourceLoader resourceLoader;

    private Environment environment;



    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 创建扫描器
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(GulimallFeign.class);
        scanner.addIncludeFilter(annotationTypeFilter);
        // 查找扫描包集合
        Map<String, Object> attrs = importingClassMetadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        if (attrs == null) {
            return;
        }
        String[] packages = (String[]) attrs.get("value");
        if (packages.length == 0) {
            packages = getDefaultPackages(importingClassMetadata);
        }
        for (String p : packages) {
            // 扫描
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(p);
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition bd = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata annotationMetadata = bd.getMetadata();
                    // 注册FactoryBean
                    registerFeignClient(registry, annotationMetadata);
                }
            }
        }
    }

    // 注册InduschainFeignClientFactoryBean
    private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata) {
        Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(GulimallFeign.class.getName());
        if (annotationAttributes == null) {
            return;
        }
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
        definitionBuilder.addPropertyValue("type", annotationMetadata.getClassName());
        definitionBuilder.addPropertyValue("name", (String) annotationAttributes.get("value"));
        AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
        registry.registerBeanDefinition(annotationMetadata.getClassName(), beanDefinition);
    }

    // 取注解所在类所在包作为扫描路径
    private String[] getDefaultPackages(AnnotationMetadata importingClassMetadata) {
        String className = importingClassMetadata.getClassName();
        int lastDotIndex = className.lastIndexOf(".");
        return lastDotIndex != -1 ? new String[]{className.substring(0, lastDotIndex)} : new String[]{};
    }

    private ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                // 目标beanDefinition必须是顶层类(不依赖与其他类isIndependent)且不能是注解
                return beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation();
            }
        };
    }

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

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

application.yaml

gulimall:
  remote:
    remote-configs:
      iot:
        url: http://127.0.0.1:8886
        authorization: 4c1f2ef1-ec18-4dfa-bfec-3a20c2f71608
        address: 127.0.0.1:8886
        token: 4c1f2ef1-ec18-4dfa-bfec-3a20c2f71608
      

最后

以上就是安详高跟鞋为你收集整理的在SpringBoot项目中封装OpenFeign写在最前封装方式的全部内容,希望文章能够帮你解决在SpringBoot项目中封装OpenFeign写在最前封装方式所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部