Feign是在RestTemplate基础上封装的,使用注解和接口来定义服务提供者以及服务提供者的接口,url风格是Rest。
简单点说,Feign 仅仅使用注解@FeignClient和接口(interface),就能定义依赖服务的接口,代替了RestTemplate的功能。
在上面讲解的Spring Cloud Ribbon中,服务消费者访问服务提供者,都是通过RestTemplate来对服务提供者的接口进行访问,
而RestTemplate已经实现了对HTTP请求的封装处理,形成了一套模版化的调用方法。在之前的例子中,
我们只是简单介绍了RestTemplate调用,但是在实际开发中,由于对服务依赖对调用可能不止于一处,
往往一个接口会被多处调用,所以我们通常都会针对各个微服务自行封装一些客户端来包装这些依赖服务的调用。
这个时候我们会发现,由于RestTemplate的封装,几乎每一个调用都是简单的模版化内容。
1、创建futurecloud-feign项目,添加Feign依赖
org.springframework.cloud
spring-cloud-starter-openfeign
2.0.2.RELEASE
2、在主函数上添加@EnableFeignClient,启动Feign
@SpringBootApplication
@EnableEurekaClient //声明为eureka 客户端
@EnableFeignClients //启动Feign
public class FuturecloudFeignApplication
{
@Bean //相当于xml中的bean标签,主要是用于调用当前方法获取到指定对象
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main( String[] args )
{
SpringApplication.run(FuturecloudFeignApplication.class,args);
}
}
3、定义一个接口,指定依赖的服务,在接口中写依赖服务的接口
package com.futurecloud.feign;
import com.futurecloud.bean.User;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("futurecloud-user") //通过注解指定依赖服务
public interface FeignInterface {
/**
* 1. 关于Feign中,GetMapping 和RequestMapping的使用问题
* C版本的spring是不能写GetMapping的,必须用RequestMapping
* 注意:必须添加@PathVariable("id") 注解
*/
@GetMapping("/user/{id}")
User getUser(@PathVariable("id") Long id);
/**
*2. 关于Feign中,传递参数的问题
* 如果传递的是复杂参数,那么Feign不管你配置的是Get请求,都会议post方式发送请求。
* 所以如果使用复杂参数(如对象),服务提供者也必须是post方式,服务消费者才可以正常访问。
* 如果服务提供者必须使用get方式,那传递多个参数时,只能以普通方式传递。
* @param user
* @return
*/
@GetMapping("/getObject")
User getObject(User user);
/**
* 3. 关于注解RequestLine的使用
* 注意:使用RequestLine的时候,参数必须使用Param注解
* @param id
* @return
*/
@RequestLine("GET /find/user/{id}")
User findUser(@Param("id") Long id);
}
4、在Feign的接口中定义依赖服务的接口时,需要注意的问题:
(1)关于Feign中,GetMapping 和RequestMapping的使用问题
Spring Cloud在Finchley版本以前,是不能写GetMapping的,必须用RequestMapping
(2)如果使用到@PathVariable ,必须指定其value
(3)关于Feign中,传递参数的问题
如果传递的是复杂参数,那么Feign不管你配置的是Get请求,都会以post方式发送请求。
所以如果使用复杂参数(如对象),服务提供者也必须是post方式,服务消费者才可以正常访问。
如果服务提供者必须使用get方式,那传递多个参数时,只能以普通方式传递。
(4)关于注解RequestLine的使用,@RequestLine是Feign的注解,使用RequestLine的时候,参数必须使用Param注解
package com.futurecloud.custom;
import feign.Contract;
import feign.Logger;
import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 类名不能跟注解名称相同
* 方法不能在启动类所在的目录,否则会被@ComponentScan注解扫描到,引起冲突
*/
@Configuration
public class FeignCustomConfig {
/**
* 修改feign的配置:使用feign.Contract代替SpringMvcContract
* 这将SpringMvc Contract 替换为feign.Contract.Default
* 即将请求的注解RequestMapping替换成RequestLine
* 注意:重写了这个配置,Feign中的接口访问方式必须使用@RequestLine
* 不重写这个配置,Feign中的接口访问方式不能使用@RequestLine,
* 否则就会报,没有定义HTTP的访问方式(GET,POST)
* @return
*/
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
/**
* 修改feign的配置:配置访问eureka的用户名、密码
* 如果Eureka添加了安全验证,则需要配置下面的用户名、密码.
* feign才能正常访问eureka中的相关信息
* @return
*/
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "123");
}
/**
* 设置日志
* 对应的在yml配置文件中设置日志级别
* @return
*/
@Bean
Logger.Level feignLoggerLevel() {
//设置日志
return Logger.Level.FULL;
}
}
package com.futurecloud.feign.interfaces;
import com.futurecloud.custom.FeignCustomConfig;
import com.futurecloud.feign.bean.User;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
//指定自定义Feign配置FeignCustomConfig
@FeignClient(name = "futurecloud-user",configuration = FeignCustomConfig.class) //通过注解指定依赖服务
public interface CustomFeignClient {
/**
* 1. 关于Feign中,GetMapping 和RequestMapping的使用问题
* Spring Cloud在C版本以前,是不能写GetMapping的,必须用RequestMapping。
* 请求参数必须写在请求路径中
* 注意:必须添加@PathVariable("id") 注解
*/
@RequestLine("GET /user/{id}") //@RequestLine:是feign的注解
// @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
User getUserById(@PathVariable("id") Long id);
/**
*2. 关于Feign中,传递参数的问题
* 如果传递的是复杂参数,那么Feign不管你配置的是Get请求,都会议post方式发送请求。
* 所以如果使用复杂参数(如对象),服务提供者也必须是post方式,服务消费者才可以正常访问。
* 如果服务提供者必须使用get方式,那传递多个参数时,只能以普通方式传递。
* @param id
* @return
*/
@RequestLine("GET /getUser/{id}") //@RequestLine:是feign的注解
//@GetMapping("/getUser/{id}")
User getUser(@PathVariable("id") Long id);
/**
* 3. 关于注解RequestLine的使用
* 注意:使用RequestLine的时候,参数必须使用Param注解
* 使用RequestLine注解的时候,请求方式GET或POST必须大写,后面是空格
* @param id
* @return
*/
@RequestLine("POST /find/user/{id}") //@RequestLine:是feign的注解
// @RequestMapping(value = "/find/user/{id}",method = RequestMethod.POST)
User findUserById(@Param("id") Long id);
}
server:
port: 8905
spring:
application:
name: futurecloud-feign-custom
#将此服务注册到eureka 服务上
eureka:
client:
serviceUrl:
defaultZone: http://user:123@localhost:10000/eureka
instance:
prefer-ip-address: true #将注册到eureka服务的实例使用ip地址
#Feign日志的配置
logging:
level:
com.futurecloud.feign.interfaces.CustomFeignClient: DEBUG
在上面的Feign配置中,我们使用name指定了所依赖的服务
@FeignClient(name = "futurecloud-user",configuration = FeignCustomConfig.class)
我们还可以使用url来制定依赖服务的服务提供者url,不使用name
@FeignClient(url = "http://localhost:10001",configuration = FeignCustomConfig.class) //通过注解指定依赖服务