声明式调用OpenFeign

本文详细介绍了如何在SpringCloud中使用OpenFeign进行声明式调用,包括添加依赖、定义客户端接口、主类配置及YML配置。通过OpenFeign,可以简化服务间的REST调用,提高代码可读性。同时,展示了如何自定义请求拦截器以增强功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

你未必出类拔萃,但一定与众不同

声明式调用-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异常
configurationOpenFeign客户端接口的配置类
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客户端接口的所有请求进行拦截了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼爱吃柚子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值