使用Feign调用注解组件(实现字段赋值功能)

目录
  • 使用效果
  • 优点
  • 如何装配
  • 特殊需求

使用注解的形式,装配在id字段,自动调用fegin赋值给目标字段。

使用效果

1.先给vo类中字段添加注解

2.调用feignDataSetUtils.setData 方法  将vo类放入 比如我的

feignDataSetUtils.setData(Stream.of(vo).collect(Collectors.toList()));

调用前

调用后 产生赋值。

利用类字段注解的形式配置好对应的fegin关系,达到自动调用fegin的效果。

优点

1.省略大部分代码,只需配置注解,和编写fegin所需方法。

2.无其他重依赖,适应性强。

3.随意装配,不需要vo类或者fegin类继承任何接口。

如何装配

加入所有工具类后,只需两步。

先加入 以下类

ApplicationContextProvider:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @Version 1.0
 * @Author:yanch
 * @Date:2021-9-28
 * @Content:
 */

@Component
public class ApplicationContextProvider implements ApplicationContextAware {
    private static ApplicationContext applicationContextSpring;

    @Override
    public synchronized void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        applicationContextSpring = applicationContext;
    }

    /**
     * 通过class 获取Bean
     */
    public static <T> T getBean(Class<T> clazz) {
        return applicationContextSpring.getBean(clazz);
    }
}

FeignColum:

import java.lang.annotation.*;

/**
 * @Version 1.0
 * @Author:yanch
 * @Date:2021-9-27
 * @Content:
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignColum {
    /**
     * 目标字段 当前vo类的想要赋值的字段名称
     *
     */
    String targetFieldName();

    /**
     *  当前字段如果是string类型且是用“,”分割的话就可以使用这个属性 可以设置为“,”,该字段将会被“,”分割
     *
     */
    String split() default "";

    /**
     *  使用的feignType枚举类型
     *
     */
    FeignType feignType();
}

FeignDataSetUtils:

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Version 1.0
 * @Author:yanch
 * @Date:2021-9-28
 * @Content:
 */
@Component
public class FeignDataSetUtils {

//    @Value("${appId}")
    private String appId;  

    /**
     * 补充User数据
     *
     * @param voList
     * @return
     */
    public List setData(List voList) {
        if (CollectionUtils.isEmpty(voList)) {
            return voList;
        }
        Object object = voList.get(0);
        Class objectClass = object.getClass();
        // 获得feign的class为key Field集合为value的map
        Map<FeignType, List<Field>> feignFieldsMap = getFieldsAnnotationMap(objectClass);
        // 获得数据dataMap 后面用来发送给feign
        Map<FeignType, List<Object>> idDataMap = buildDataMap(feignFieldsMap.keySet());
        // 遍历所有注解
        // 遍历所有携带注解的字段-获得id集合
        putIdDataMap(feignFieldsMap, voList, idDataMap);

        // Feign返回结果集合
        Map<FeignType, Map<Object, Object>> feignResultMap = getFeignResultMap(idDataMap);
        // 遍历所有
        // 遍历所有携带注解的字段-添加集合
        putDataMap(feignFieldsMap, objectClass, voList, feignResultMap);
        return voList;
    }

    /**
     * 添加Feign的Result数据
     * @return
     */
    private Map<FeignType, Map<Object, Object>> getFeignResultMap(Map<FeignType, List<Object>> idDataMap) {
        // 初始化Feign返回结果集合
        Map<FeignType, Map<Object, Object>> feignResultMap = new HashMap();

        Map<FeignType, IFeignFunction> feignFunctionMap = FeignFunctionMap.getFeignFunctionMap();
        idDataMap.keySet().forEach(feignType -> {
            IFeignFunction feignFunction = feignFunctionMap.get(feignType);
            Optional.ofNullable(feignFunction).ifPresent(m -> {
                m.setAppId(appId);
                m.setFeign(ApplicationContextProvider.getBean(feignType.getFeignClass()));
                Optional.ofNullable(idDataMap.get(feignType)).ifPresent(idList ->
                                feignResultMap.put(feignType, m.getBatch(idList))
                        );
            });
        });

//        // 获得用户集合
//        Map<String, Object> userVoMap= Optional.ofNullable(idDataMap.get(FeignType.UserInfoFeign.getFeignClass())).map(m->userInfoFeign.getBatch(m,appId).stream().collect(Collectors.toMap(UserVo::getId, my->(Object)my))).orElse(null) ;
//        Optional.ofNullable(userVoMap).ifPresent(p->
//                        feignResultMap.put(FeignType.UserInfoFeign.getFeignClass(),p)
//                );

        return feignResultMap;
    }

    /**
     * 遍历所有携带注解的字段-获得id集合
     *
     * @return
     */
    private void putIdDataMap(Map<FeignType, List<Field>> feignFieldsMap, List voList, Map<FeignType, List<Object>> idDataMap) {
        //遍历所有数据
        voList.stream().forEach(entry -> {
            feignFieldsMap.keySet().stream().forEach(feignClass -> {
                feignFieldsMap.get(feignClass).stream().forEach(field -> {
                    FeignColum colum = field.getAnnotation(FeignColum.class);
                    field.setAccessible(true);
                    // 开始添加id数据
                    try {
                        if (StringUtils.isEmpty(colum.split())) {
                            Optional.ofNullable(field.get(entry)).filter(f -> !ObjectUtils.isEmpty(f)).ifPresent(
                                    fieldValue -> idDataMap.get(colum.feignType()).add(fieldValue));
                        } else {
                            Optional.ofNullable(field.get(entry)).map(m -> (String) m).filter(f -> StringUtils.isNotEmpty(f)).ifPresent(
                                    fieldValue -> idDataMap.get(colum.feignType()).addAll(Arrays.stream(fieldValue.split(colum.split())).collect(Collectors.toList())));
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                });
            });
        });
        // 删除没有的数据
        idDataMap.values().removeIf(value -> CollectionUtils.isEmpty(value));
    }

    /**
     * 遍历所有携带注解的字段-添加集合
     *
     * @return
     */
    private void putDataMap(Map<FeignType, List<Field>> feignFieldsMap, Class objectClass, List voList, Map<FeignType, Map<Object, Object>> resultMap) {
        if (CollectionUtils.isEmpty(feignFieldsMap) || CollectionUtils.isEmpty(resultMap)) {
            return;
        }
        voList.stream().forEach(entry -> {
            feignFieldsMap.keySet().stream().forEach(feignType -> {
                Map<Object, Object> voMap = resultMap.get(feignType);
                feignFieldsMap.get(feignType).stream().forEach(field -> {
                    try {
                        FeignColum colum = field.getAnnotation(FeignColum.class);
                        String targetFieldName = colum.targetFieldName();
                        // 目标字段
                        Field targetField = objectClass.getDeclaredField(targetFieldName);
                        targetField.setAccessible(true);
                        // 开始添加用户数据
                        if (StringUtils.isEmpty(colum.split())) {
                            Optional.ofNullable(field.get(entry)).filter(f -> !ObjectUtils.isEmpty(f)).ifPresent(
                                    fieldValue -> {
                                        Object object = voMap.get(fieldValue);
                                        try {
                                            targetField.set(entry, object);
                                        } catch (IllegalAccessException e) {
                                            e.printStackTrace();
                                        }
                                    });
                        } else {
                            Optional.ofNullable(field.get(entry)).map(m -> (String) m).filter(f -> StringUtils.isNotEmpty(f)).ifPresent(
                                    fieldValue -> {
                                        try {
                                            Object object = Arrays.stream(fieldValue.split(colum.split())).map(m -> {
                                                return voMap.get(m);
                                            }).collect(Collectors.toList());
                                            targetField.set(entry, object);
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                    });
                        }
                    } catch (NoSuchFieldException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                });
            });
        });
    } 

    /**
     * 获得需要的注解字段
     *
     * @param cls
     * @return
     */
    private Map<FeignType, List<Field>> getFieldsAnnotationMap(Class cls) {
        // 注解集合对象
        Map<FeignType, List<Field>> feignMap = buildAnnotationMap();
        // 字段遍历
        Arrays.stream(cls.getDeclaredFields()).forEach(field -> {
            feignMap.keySet().stream().forEach(feignClass -> {
                if (field.isAnnotationPresent(FeignColum.class)) {
                    FeignColum colum = field.getAnnotation(FeignColum.class);
                    if(colum.feignType()!=feignClass){
                        return;
                    }
                    feignMap.get(colum.feignType()).add(field);
                }
            });
        });
        // 删除没有的字段注解
        feignMap.values().removeIf(value -> CollectionUtils.isEmpty(value));
        return feignMap;
    }

    /**
     * 初始化注解map
     *
     * @return
     */
    private Map<FeignType, List<Field>> buildAnnotationMap() {
        return Arrays.stream(FeignType.values()).collect(Collectors.toMap(my -> my, my -> new ArrayList()));
    } 

    /**
     * 初始化字段数据map
     *
     * @return
     */
    private Map<FeignType, List<Object>> buildDataMap(Collection<FeignType> collection) {
        return collection.stream().collect(Collectors.toMap(my -> my, my -> new ArrayList()));
    }
}

IFeignFunction:

import lombok.Data;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

/**
 * @Version 1.0
 * @Author:yanch
 * @Date:2021-9-28
 * @Content:
 */
@Data
public abstract class IFeignFunction<T,E> {
    Class<T> clazz;
    T feign;
    String appId;
    public IFeignFunction(){
        doGetClass();
    }
    public abstract Map<E, Object> getBatch(List<E> idList);

    public void doGetClass() {
        Type genType = this.getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        this.clazz = (Class<T>) params[0];
    }
}

剩下的两块代码需要自己加东西

  • FeignType:这个是用来给注解配置Feign的枚举选项,也就是你想要什么Feign就需要在FeignType中添加一次。

例如我的:

/**
 * @Version 1.0
 * @Author:yanch
 * @Date:2021-9-27
 * @Content:
 */
public enum FeignType {
    /**
     *手工配置1   UserInfoFeign 是选项名称 用来给注解配置使用
     */
    UserInfoFeign(),
    /**
     *手工配置2   UserInfoFeign2 是选项名称 用来给注解配置使用
     */
    UserInfoFeign2();
}
  • FeignFunctionMap:它的作用是用来绑定FeignType和IFeignFunction(Feign的方法)的关系。
  • 具体可以查看代码理解。比如代码里面的put(FeignType.UserInfoFeign 。。。。和put(FeignType.UserInfoFeign2.。。。。
import com.xxx.xxx.sdk.feign.UserInfoFeign;
import com.xxx.xxx.sdk.vo.UserVo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; 

/**
 * @Version 1.0
 * @Author:yanch
 * @Date:2021-9-28
 * @Content:
 */
public class FeignFunctionMap {
    private static Map<FeignType, IFeignFunction> feignFunctionMap = new HashMap();
    static {
        /**
         * 用来绑定FeignType.UserInfoFeign 和Feign方法的关系(IFeignFunction)
         *手工配置1
         * IFeignFunction的第一个泛型是Feign的类,第二个是字段参数类型。 比如我用的是String 存储的id,这里就用String
         */
        // 用户Feign
        put(FeignType.UserInfoFeign,new IFeignFunction<UserInfoFeign,String>() {
            @Override
            public  Map<String, Object> getBatch(List<String> idList) {
                // feign对象相当于UserInfoFeign的实例化对象,appid你们可以不用管,这个是我的feign必须要携带的一个常量。
                // 为什么要返回一个Map<String, Object> ?  因为这将要用来做成字典,key是id,value是这个feign根据这个id调用来的值
                return feign.getBatch(idList, appId).stream().collect(Collectors.toMap(UserVo::getId, my -> (Object) my));
            }
        });
        /**
         * 用来绑定FeignType.UserInfoFeign2 和Feign方法的关系(IFeignFunction)
         *手工配置2
         * IFeignFunction的第一个泛型是Feign的类,第二个是字段参数类型。 比如我用的是Long 存储的id,这里就用Long
         */
        put(FeignType.UserInfoFeign2,new IFeignFunction<UserInfoFeign,Long>() {
            @Override
            public  Map<Long, Object> getBatch(List<Long> idList) {
                return feign.getBatch(idList.stream().map(m->String.valueOf(m)).collect(Collectors.toList()), appId).stream().collect(Collectors.toMap( my ->Long.valueOf(my.getId()), my -> (Object) my));
            }

        });
    }  

    /**
     *--------------------------以下无需配置
     */
    /**
     *@param feignType FeignType名称
     *@param iFeignFunction feign方法实现方式
     */
    public  static void put(FeignType feignType,IFeignFunction iFeignFunction){
        feignFunctionMap.put(feignType,iFeignFunction);
    }

    public static Map<FeignType, IFeignFunction> getFeignFunctionMap() {
        return feignFunctionMap;
    }
}

如果把自己的FeignType和FeignFunctionMap配置完成后就可以在自己的类中加入注解了。

比如下面是我的vo类。

之后放入feignDataSetUtils.setData(Stream.of(vo).collect(Collectors.toList()))当中就能够赋值了。

上面的代码没有必要放出来所以我就已图片的形式展示出来了。

FeignColum注解类的三个属性用意:

  • targetFieldName:用来记录目标赋值的字段名称
  • split:用来切割字符串的分割符号,比如我的 字段是allUserId的值是 "1;2"  那我就需要配置        split = ";",且目标字段也必须是List接收。
  • feignType:用来绑定使用的feginType的枚举类型。

特殊需求

1.假如我的feign的方法每次请求除了携带id还需要携带一个常量参数访问该怎么办?

这个可以是用全局搜索参看 appId的使用方式。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 详解SpringCloud-OpenFeign组件的使用

    思考: 使用RestTemplate+ribbon已经可以完成服务间的调用,为什么还要使用feign? String restTemplateForObject = restTemplate.getForObject("http://服务名/url?参数" + name, String.class); 存在问题: 1.每次调用服务都需要写这些代码,存在大量的代码冗余 2.服务地址如果修改,维护成本增高 3.使用时不够灵活 说明 https://cloud.spring.io/sprin

  • SpringCloud之@FeignClient()注解的使用方式

    目录 @FeignClient()注解的使用 @FeignClient标签的常用属性如下 SpringCloud 服务间互相调用 @FeignClient注解 我在FEIGN-CONSUMER 在FEIGN-CONSUMER 这是项目中的Controller层 @FeignClient()注解的使用 由于SpringCloud采用分布式微服务架构,难免在各个子模块下存在模块方法互相调用的情况.比如service-admin服务要调用service-card 服务的方法. @FeignClient

  • Java之Springcloud Feign组件详解

    一.Feign是什么? OpenFeign是Spring Cloud提供的一个声明式的伪Hltp客户端,它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可,Nacos很好的兼容了OpenFeign,OpenFeign默认集成了Ribbon, 所以在Nacos下使用OpenFeign默认就实现了负载均衡的效果. 二.使用步骤 1.消费方导入依赖 ···c org.springframework.cloud spring-cloud-starter-openfeign

  • 使用Feign调用注解组件(实现字段赋值功能)

    目录 使用效果 优点 如何装配 特殊需求 使用注解的形式,装配在id字段,自动调用fegin赋值给目标字段. 使用效果 1.先给vo类中字段添加注解 2.调用feignDataSetUtils.setData 方法  将vo类放入 比如我的 feignDataSetUtils.setData(Stream.of(vo).collect(Collectors.toList())); 调用前 调用后 产生赋值. 利用类字段注解的形式配置好对应的fegin关系,达到自动调用fegin的效果. 优点 1

  • 微服务之间如何通过feign调用接口上传文件

    具体需求: 我们的项目是基于springboot框架的springcloud微服务搭建的,后端服务技术层面整体上分为business服务和core服务,business服务用于作为应用层,直接连接客户端,通常用于聚合数据,core服务用来客户端具体操作不同需求来控制数据库,文件上传是通过客户端上传接口,通过business服务,由服务端调用feign接口,也是第一次做这种文件中转,遇到各种问题,下面是我自己的解决方案,不喜勿喷,代码小白一枚; 一.core服务层接口@requestmapping

  • 在CRUD操作中与业务无关的SQL字段赋值的方法

    提高效率一直是个永恒的话题,编程中有一项也是可以提到效率的,那就是专注做一件事情,让其它没有强紧密联系的与之分开.这里分享下我们做CRUD时遇到的常见数据处理场景: •数据库表字段全部设计为非空,即使这个字段在业务上是可以为空的,之所以将数据库表字段全部设计为非空,这里有优点也有缺点,我们认为优点大于缺点,所以选择了它 优点: 1.获取值时,不用判断这个字段是否为null,直接可用于逻辑运算. 2.mysql DBA推荐此方案,可能是有利于性能,这里我并非求证过. 缺点: 1.业务含义没有nul

  • SpringBoot使用Feign调用其他服务接口

    使用SpringCloud的Feign组件能够为服务间的调用节省编码时间并提高开发效率,当服务本身不复杂时可以单独将该组件拿出使用. 引入依赖 <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign --> <dependency> <groupId>org.springframework.cloud</groupId>

  • Feign调用服务各种坑的处理方案

    1.编写被调用服务 @RefreshScope @RestController public class XXXController extends BaseController implements IndicatorsFeignApi{ @Resource private XXXService xxx; @Override public Wrapper<CommonVo> getXXXX(@RequestBody CommonDto commonDto) { try { CommonVo

  • 基于springboot服务间Feign调用超时的解决方案

    解决springboot服务间Feign调用超时问题概述 1.起因 在完成项目功能需求的开发,经过自己测试以及通过测试组测试通过后,昨晚正式部署到线上环境进行正式运行前的最后一次的测试.但是在测试中,由A服务调用B服务接口时,***通过Feign调用(其实就是http请求,当A服务调用B服务时,如果不配置超时时间,那么A发出请求后,B应该立即响应,否则A服务会认为B已经断开连接)出现***连接超时的错误,错误信息:Read timed out- 2.原因 用idea开发debug模式调试代码时,

  • springcloud本地调试feign调用出现的诡异404问题及解决

    目录 本地调试feign调用出现的诡异404问题 问题产生 技术框架 核心代码 诡异的404 心态 springcloud在本地调试的踩坑记录 1.在本地调试的时候 2.修改配置文件中关于eureka的配置 3.还是关于eureka的配置 本地调试feign调用出现的诡异404问题 问题产生 最近在给公司准备做分布式事务框架seata的调研,准备搭建一套demo,根据阿里云官网的案例,我准备搭建一套微服务架子,分别含有business.order.storage三个微服务组成,其中第一个微服务实

  • C#实现利用反射简化给类字段赋值的方法

    本文实例讲述了C#实现利用反射简化给类字段赋值的方法.分享给大家供大家参考.具体分析如下: 说明:这个例子主要的思路是建立一个类和数据库查询语句的字段结构是一致的 然后利用反射,直接用数据字段名称进行拼凑,给类对象的字段进行赋值   1.类的定义 namespace CCB_Donet.ClassFolder { public class FieldRuleInfo { public string gStrFNo; public string gStrFName; public string g

  • Angular父组件调用子组件的方法

    理解组件 组件是一种特殊的指令,使用更简单的配置项来构建基于组件的应用程序架构 这样他能简单地写app,通过类似的web Component 或者angular2的样式. web Component 是一个规范.马上就要成为标准. 应用组件的优点: 比普通指令配置还简单 提供更好的默认设置和最好的实践 对基于组建的应用架构更优化. 对angular2的升级更平滑. 不用组建的情况: 对那些在 compile或者pre-link阶段要执行操作的指令,组件不能用,因为无法到达那个阶段. 如果你想定义

  • MybatisPlus 不修改全局策略和字段注解如何将字段更新为null

    mybatis-plus 以下简称mp,目前应该也算是主流的一款数据访问层应用框架.源于其对mybatis 的近乎完美的封装,让我们在使用的时候无比的顺滑, 几乎提供了所有单表操作的方法,大大提升了效率.并且这款框架还是国产的哦,没了解过的可以去了解一下. 回归正题,我们这次来讲一下,怎么样通过mp将数据库中的一个字段更新为null. 可能很多人会觉得奇怪,更新为null, 直接set field = null 不就可以了.这里大家要注意一下,一般情况,我们在使用mp的时候,他的默认策略是空不更

随机推荐