Feign 是 Netflix 公司开发的一个声明式的 REST 调用客户端;
顾名思义,声明式就是只做声明,不作具体实现的方式。
即只需要告诉计算机,你想要得到什么样的结果,计算机则会给你想要的结果。
<!--feign客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@MapperScan("cn.mapper")
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
接口口方法类似Controller,不同是无具体实现。
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
这个客户端主要是基于SpringMVC的注解来声明远程调用的信息,比如:
服务名称:userservice
请求方式:GET
请求路径:/user/{id}
请求参数:Long id
返回值类型:User
这样,Feign就可以帮助我们发送http请求,无需自己使用RestTemplate来发送了。
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.用Feign远程调用
User user = userClient.findById(order.getUserId());
// 3.封装user到Order
order.setUser(user);
// 4.返回
return order;
}
}
将Feign的Client抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用。
例如,将UserClient、User、Feign的默认配置都抽取到一个feign-api包中,所有微服务引用该依赖包,即可直接使用。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
import feign.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
import lombok.Data;
@Data
public class User {
private Long id;
private String username;
private String address;
}
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfiguration {
@Bean
public Logger.Level logLevel(){
return Logger.Level.BASIC;
}
}
<!--引入feign的统一api-->
<dependency>
<groupId>cn.itcast.demo</groupId>
<artifactId>feign-api</artifactId>
<version>1.0</version>
</dependency>
import cn.feign.clients.UserClient;
import cn.feign.pojo.User;
import cn.order.mapper.OrderMapper;
import cn.order.pojo.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.用Feign远程调用
User user = userClient.findById(order.getUserId());
// 3.封装user到Order
order.setUser(user);
// 4.返回
return order;
}
}
两种解决方式:
1、使用注解@EnableFeignClients(basePackages = “cn.feign.clients”),指定userClient的所在包,包下所有类均被扫描
2、使用@EnableFeignClients(clients = UserClient.class),指定userClient的字节码,仅扫描指定类。
导入多个时,可以@EnableFeignClients(clients = {UserClient.class,orderClient.class})。(推荐该方式)
优点: 面向契约编程,简单
缺点: 会造成服务端与消费者在API层面的紧耦合;且对SpringMVC不起作用,因为参数列表中的注解映射并不会继承(例如:@PathVariable),因此Controller中必须再次声明方法、参数列表、注解。