原生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组件(含源码分析)