你未必出类拔萃,但一定与众不同
声明式调用-OpenFeign
文章目录
OpenFeign是一种声明式调用,我们只需要按照一定的规则,描述我们的接口,就能帮我们完成REST风格的调用,减少代码的可用量,提高代码的可读性,SpringCloud 将OpenFeign封装后,给出的规则完全采用了SpringMVC的风格,只要熟悉SpringMVC很容易就能完成接口的描述以及服务调用的功能
OpenFeign的使用
1.加入Feign 和 Hystrix依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
2.声明OpenFeign的客户端接口(用以前学习springCloud的项目)
/**
* @author Yu W
* @version V1.0
* @ClassName
* @Description:
* @date 2021/2/15 14:22
*/
@FeignClient("SPRINGCLOUD-PROVIDER-USER")
public interface UserFacade {
/**
*查找全部用户
* @return
*/
@GetMapping("/user/getAll")
List<UserList> queryAllUser();
/**
*删除一个用户
* @param userId
* @return
*/
@GetMapping("/user/deleteInfo/{userId}")
int deleteUser(@PathVariable(value = "userId") String userId);
/**
* 根据id查找用户
* @param userId
* @return
*/
@GetMapping("/user/userInfo/{userId}")
UserList queryUser(@PathVariable(value = "userId") String userId);
/**
* 修改一个用户
* @param userList
* @return
*/
@PutMapping("/user/userInfo")
int updateUser(@RequestBody UserList userList);
}
-
@FeignClient(“SPRINGCLOUD-PROVIDER-USER”):表示这个接口是一个OpenFeign的客户端,底层使用Ribbon执行REST风格的调用,配置的SPRINGCLOUD-PROVIDER-USER代表一个微服务的名称,也就是准备调用的微服务
-
@GetMapping("/user/getAll"):表示调用HTTP的GET请求调用用户微服务,而/user/getAll则表示URI
-
@GetMapping("/user/userInfo/{userId}")中的userId则表示一个参数占位,因此使用@PathVariable(value = “userId”)修改参数userId 来和这个参数对应
-
@PutMapping("/user/userInfo"):表示使用HTTP的PUT请求调用用户微服务,对于这个请求需要一个JSON的请求体,因此参数使用@RequestBody修饰UserList类型参数,表示将参数转换为JSON请求体。
3.定义一个OpenFeign接口
/**
* @author Yu W
* @version V1.0
* @ClassName
* @Description:
* @date 2021/2/15 14:29
*/
@RestController
@RequestMapping("/feign")
public class UserController {
@Autowired
private UserFacade userFacade = null;
/**
*查找全部用户
* @return
*/
@GetMapping("/user/getAll")
public ResultMap queryAllUser(){
List<UserList> userLists = userFacade.queryAllUser();
ResultMap resultMap = ResultMap.builder()
.code(200)
.msg("查询所有用户成功")
.result("SUCCESS")
.dataArray(Collections.singletonList(userLists))
.build();
return resultMap;
}
/**
*删除一个用户
* @param userId
* @return
*/
@GetMapping("/user/deleteInfo/{userId}")
public ResultMap deleteUser(@PathVariable(value = "userId") String userId){
int flag = userFacade.deleteUser(userId);
if(flag>0){
ResultMap resultMap = ResultMap.builder()
.code(200)
.msg("删除成功")
.result("SUCCESS")
.build();
return resultMap;
}else{
ResultMap resultMap = ResultMap.builder()
.code(200)
.msg("删除失败")
.result("ERROR")
.build();
return resultMap;
}
}
/**
* 根据id查找用户
* @param userId
* @return
*/
@GetMapping("/user/userInfo/{userId}")
public ResultMap queryUser(@PathVariable(value = "userId") String userId){
UserList userList = userFacade.queryUser(userId);
ResultMap resultMap = ResultMap.builder()
.code(200)
.msg("查询用户成功")
.result("SUCCESS")
.formData(userList)
.build();
return resultMap;
}
/**
* 修改一个用户
* @param userList
* @return
*/
@PutMapping("/user/userInfo")
public ResultMap updateUser(@RequestBody UserList userList){
int flag = userFacade.updateUser(userList);
if(flag>0){
ResultMap resultMap = ResultMap.builder()
.code(200)
.msg("删除成功")
.result("SUCCESS")
.build();
return resultMap;
}else {
ResultMap resultMap = ResultMap.builder()
.code(200)
.msg("删除失败")
.result("ERROR")
.build();
return resultMap;
}
}
}
4.此时还需要修改当前模块的主类
/**
* @author Yu W
* @version V1.0
* @ClassName
* @Description:
* @date 2021/2/13 17:24
*/
@SpringBootApplication
@EnableCircuitBreaker
@EnableFeignClients(basePackages = "com.bluedot")
@EnableEurekaClient //启动之后自动注册到服务端
public class SpringBootConsumer {
public static void main(String[] args) {
SpringApplication.run(SpringBootConsumer.class,args);
}
}
@EnableFeignClients(basePackages = “com.bluedot”) 表示对@FeignClient接口进行扫描,并且装配到IoC容器之中
5.访问
http://localhost:8888/feign/user/getAll
结果截图
OpenFeign客户端的配置
配置OpenFeign的方法有很多种,最直观的当属在注解@FeignClient上配置,关于@FeignClient配置项的解释如下
配置项 | 说明 |
---|---|
value | 配置客户端名称 一般为微服务名称 |
name | 配置客户端名称 一般为微服务名称 |
serviceId | 不推荐使用 |
qualifier | 配置SpringBean名称 |
url | 配置抽象的url或者具体的url |
decode404 | 发生HTTP的404错误时,是否解码而非抛出FeignException异常 |
configuration | OpenFeign客户端接口的配置类 |
fallback | 指定Hystrix的降级服务类 |
fallbackFactory | 指定降级工厂 |
path | 请求路径加入前缀 |
primary | 对外部注入是否设置当前Bean为首要Bean |
FeignClientsConfiguration源码
@Configuration
public class FeignClientsConfiguration {
//springmvc消息转换工厂
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
//springmvc的参数处理器
@Autowired(
required = false
)
private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList();
@Autowired(
required = false
)
//OpenFeign的参数格式器
private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList();
@Autowired(
required = false
)
private Logger logger;
public FeignClientsConfiguration() {
}
//OpenFeign的解码器
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
//先经过springMVC消息转换器工厂处理
return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingClass({"org.springframework.data.domain.Pageable"})
public Encoder feignEncoder() {
//先经过springMVC消息转换器工厂处理
return new SpringEncoder(this.messageConverters);
}
@Bean
@ConditionalOnClass(
name = {"org.springframework.data.domain.Pageable"}
)
@ConditionalOnMissingBean
public Encoder feignEncoderPageable() {
//先经过springMVC消息转换器工厂处理
return new PageableSpringEncoder(new SpringEncoder(this.messageConverters));
}
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}
@Bean
public FormattingConversionService feignConversionService() {
FormattingConversionService conversionService = new DefaultFormattingConversionService();
Iterator var2 = this.feignFormatterRegistrars.iterator();
while(var2.hasNext()) {
FeignFormatterRegistrar feignFormatterRegistrar = (FeignFormatterRegistrar)var2.next();
feignFormatterRegistrar.registerFormatters(conversionService);
}
return conversionService;
}
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
@Bean
@ConditionalOnMissingBean({FeignLoggerFactory.class})
public FeignLoggerFactory feignLoggerFactory() {
return new DefaultFeignLoggerFactory(this.logger);
}
@Bean
@ConditionalOnClass(
name = {"org.springframework.data.domain.Page"}
)
public Module pageJacksonModule() {
return new PageJacksonModule();
}
@Configuration
@ConditionalOnClass({HystrixCommand.class, HystrixFeign.class})
protected static class HystrixFeignConfiguration {
protected HystrixFeignConfiguration() {
}
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(
name = {"feign.hystrix.enabled"}
)
public Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
}
}
从 FeignClientsConfiguration的源码可以了解到OpenFeign的配置,在通过编码器解码器协议之前都会经过SpringMVC消息转换器工厂进行处理,这样OpenFeign就能处理各种各样的SpringMVC请求,从而支持文件解析。
除了可以通过@FeignClient配置,还可以通过YML的方式进行配置,
配置的config是一个map。键是微服务的名称,值是一个客户端的配置类FeignClientsConfiguration
YML配置OpenFeign客户端接口实例
feign:
client:
#默认配置key,默认值为default
default-config: default
#是否启用默认的属性配置机制
default-to-properties: true
config:
#配置default 启用全局OpenFeign客户端接口默认配置
default:
#发送HTTP的404是否抛出异常
decode404: false
#读取请求超时时间
read-timeout: 5000
#重试器全限定名
retryer: xxx
#OpenFeign协议全限定名feign.Contract接口的实现类
contract: xxx
#OpenFeign解码器全限定名feign.codec.Decoder接口的实现类
decoder: xxx
#penFeign编码器全限定名feign.codec.Encoder接口的实现类
encoder: xxx
#日志级别 四个级别
logger-lever: basic
#调用拦截器
request-interceptors: xxx,xxx,xxx
#错误解码器全限定名feign.codec.ErrorDecoder接口的实现类
error-decoder: xxxx
自定义一个简单的request-interceptors拦截器UserInterceptor
public class UserInterceptor implements RequestInterceptor {
/**
* 拦截器的意义在于 根据自己的需求进行定义RestTemplate和请求参数请求体等
* @param requestTemplate 请求模板
*/
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header("id","1");
}
}
然后在yml 的配置文件进行修改即可
#调用拦截器
request-interceptors: com.bluedot.UserInterceptor
这样就可以对OpenFeign客户端接口的所有请求进行拦截了。