Loading

原生Feign进行HTTP调用

使用原生的Feign适用于SpringMVC项目。在配置上花费了时间,并且踩了一些坑,感觉还是不太值得。愿世间没有这么多的配置。

通过7个步骤配置完成

一、添加依赖

 <!--feign的客户端-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>9.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
            <version>9.7.0</version>
        </dependency>
        <!--因为使用的是jackson 序列化,所以引入此包-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-jackson</artifactId>
            <version>9.7.0</version>
        </dependency>
        <!--客户端使用的okhttp所以引入此包-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <version>11.1</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.48.sec06</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>22.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
            <scope>provided</scope>
        </dependency>

二、Feign的配置http

/**
 * @author chris wang
 * @date 2021年04月23日 9:17
 * @description Feign http配置
 */
@Configuration
public class FeignClientConfiguration {

    @Autowired
    private Contract environmentFeignContract;

    @Bean
    public Feign feign() {
        return Feign.builder() //创建feign的构建者
                .encoder(new JacksonEncoder())  //JSON编码
                .decoder(new MyDecoder()) //JSON解码
                .options(new Request.Options(1000, 3500))  //设置超时
                .retryer(new Retryer.Default(5000, 5000, 2)).contract(environmentFeignContract).
                        build();  //设置重试
    }
}
 
/**
 * @author chris wang
 * @date 2021年04月23日 9:16
 * @description 自定义解析ApiClient注解的占位符 $viptoken
 */
public abstract class AbstractFeignContract extends Contract.Default {

    @Override
    protected void processAnnotationOnClass(MethodMetadata data, Class<?> targetType) {
        super.processAnnotationOnClass(data, targetType);
        handleHeaders(data);
    }

    @Override
    protected void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, Method method) {
        super.processAnnotationOnMethod(data, annotation, method);
        handleHeaders(data);
    }

    @Override
    protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
        return super.processAnnotationsOnParameter(data, annotations, paramIndex);
    }

    private void handleHeaders(MethodMetadata data) {
        if (data == null || CollectionUtils.isEmpty(data.template().headers())) return;
        for (Map.Entry<String, Collection<String>> entry : data.template().headers().entrySet()) {
            if (StringUtils.isEmpty(entry.getKey())) continue;
            Collection<String> values = entry.getValue();
            if (CollectionUtils.isEmpty(values)) continue;
            Collection<String> result = Lists.newArrayList();
            Collection<String> exludes = Lists.newArrayList();
            for (String val : values) {
                exludes.add(val);
                if (val.startsWith(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_PREFIX) && val.endsWith(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_SUFFIX)) {
                    String placeholder = val.substring(val.indexOf(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_PREFIX) + 2, val.indexOf(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_SUFFIX));
                    val = findProperty(placeholder);
                }
                result.add(val);
            }
            values.removeAll(exludes);
            values.addAll(result);
        }
    }

    public abstract String findProperty(String key);
}
/**
 * @author chris wang
 * @date 2021年04月27日 12:55
 * @description properties获取
 */
@Configuration
public class EnvironmentFeignContract extends AbstractFeignContract {

    @Autowired
    private Environment environment;

    @Override
    public String findProperty(String key) {
        return environment.getProperty(key);
    }
}

/**
 * @author chris wang
 * @date 2021年04月23日 9:17
 * @description 定义解码规则
 */
public class MyDecoder extends JacksonDecoder {

    @Override
    public Object decode(Response response, Type type) throws IOException {
        //包装一个IocResponse<T>
        Type wapperType = ParameterizedTypeImpl.make(MyResponse.class, new Type[]{
                type
        }, null);
        Object decodeObj = super.decode(response, wapperType);
        if (decodeObj != null && decodeObj instanceof MyResponse) {
            MyResponse iocResponse = (MyResponse) decodeObj;
            if (!iocResponse.isSuccess()) throw new RuntimeException(iocResponse.getMessage());
            return iocResponse.getData();
        }
        throw new RuntimeException(JSON.toJSONString(decodeObj));
    }
}

三、定义一个ApiClient

/**
 * @author chris wang
 * @date 2021年04月23日 9:16
 * @description api接口client
 */
public interface ApiClient {

    @Headers({"Content-Type: application/json", "Accept: application/json", "viptoken: ${viptoken}"})
    @RequestLine("POST /my/api/system_metric/continuous_build")
    List<ContinuousBuildResp> getContinuousBuild(ContinuousBuildReq req);
}

四、定义一个Client工厂

/**
 * @author chris wang
 * @date 2021年04月23日 9:16
 * @description 定义一个Client工厂
 */
@Configuration
public class FeignClientApiFactory {

    @Value("${myUrl}")
    private String myUrl;

    @Autowired
    private Feign feign;

    @Bean
    public ApiClient getMyApi() {
        return feign.newInstance(new Target.HardCodedTarget<>(ApiClient.class, myUrl));
    }
}

五、泛型Response

/**
 * @author chris wang
 * @date 2021年04月27日 9:28
 * @description 使用泛型的一个返回
 */
public class MyResponse<T> {

    private String message;
    private boolean success;
    private T data;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

六、其他类

/**
 * @author chris wang
 * @date 2021年04月27日 9:28
 * @description
 */
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ContinuousBuildResp {

    private int ci_num;
    private int ci_score;
    private int ci_success_num;
    private String ci_success_rate;
    private String rank;
}
/**
 * @author chris wang
 * @date 2021年04月27日 9:28
 * @description
 */
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ContinuousBuildReq {

    private String date_type;
    private String start_time;
    private String end_time;
}

七、application.properties

myUrl=http://localhost:8090
viptoken=xxxxxxxxx

参考引用:

使用原生的Feign进行HTTP调用
如何通过HTTP优雅调用第三方-Feign
教你如何在SpringMVC项目中单独使用Feign组件(含源码分析)

posted @ 2021-05-06 22:56  FynnWang  阅读(664)  评论(0编辑  收藏  举报