一、value,name
这两个属性的作用是一样的,如果没有配置url,那么配置的值将作为服务的名称,用于服务的发现,反之只是一个名称。
注意:这里写的是你要调用的那个服务的名称,而不是你自己的那个服务的名称。另外,如果同一个工程中出现两个接口使用一样的服务名称会报错。原因是Client名字注册到容器中重复了。
Description:
The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
FeignCilent注解注入到容器中底层源码默认首先使用的是属性value的值作为bean的名称注入到Spring容器中。
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
private String getClientName(Map<String, Object> client) {
if (client == null) {
return null;
}
String value = (String) client.get("contextId");
if (!StringUtils.hasText(value)) {
value = (String) client.get("value");
}
if (!StringUtils.hasText(value)) {
value = (String) client.get("name");
}
if (!StringUtils.hasText(value)) {
value = (String) client.get("serviceId");
}
if (StringUtils.hasText(value)) {
return value;
}
throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
+ FeignClient.class.getSimpleName());
}
二、contextId
想要解决一种出现的问题有两种方式,
一、在配置文件中加入下面的配置,作用是允许出现beanName一样的BeanDefinition,可以解决一种报错问题。
spring.main.allow-bean-definition-overriding=true
二、每一个Client手动指定不同的ContextId,也可以解决这和问题。
通过一种源码可以知道,如果配置了contextId这个属性,就会采用contextId作为bean的名称注入进容器中,如果没有配置就会去找value然后是name,最后是serviceId(此属性yijing废弃)。
另外在注册FeignClient中,这个属性还会作为Client别名的一部分,如果配置了qualifier,会有限使用qualifier作为别名。
// 拼接别名
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
// null
beanDefinition.setPrimary(primary);
// 配置了qualifier优先用qualifier
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
三、url
用于配置指定的地址,相当于使用http的形式直接请求这个服务,不经过注册中心。如果配置了这个属性和,name属性(通过注册中心调用目标服务)将会被覆盖,不会通过配置中心调用服务。
四、configuration
这个属性是配置Feign的配置类,在配置类中可以定义Feign的Encoder、Decoder、loglevel、contract、鉴权信息等。
public class FeignConfiguration {
@Bean
public Logger.Level getLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password");
}
@Bean
public CustomRequestInterceptor customRequestInterceptor() {
return new CustomRequestInterceptor();
}
// Contract,feignDecoder,feignEncoder.....
}
使用如下:
@FeignClient(value = "optimization-user", configuration = FeignConfiguration.class)
五、fallback /fallbackFactory
定义服务降级、容错的处理类。fallback必须实现使用了FeignClient注解中的接口,否者无法实现兜底。
两种方式:
一、直接实现对应的接口
@Component
public class UserRemoteClientFallback implements UserRemoteClient {
@Override
public User getUser(int id) {
return new User(0, "默认fallback");
}
}
使用方式:
@FeignClient(value = "optimization-user", fallback = UserRemoteClientFallback.class)
这种就无法知道降级的具体原因
二、实现FallbackFactory接口
public class TestFeignFallback implements FallbackFactory<TestClient> {
@Override
public TestClient create(Throwable cause) {
return new TestClient() {
@Override
public String callTest(String content,String auth) {
log.error("call test error:{}",JacksonUtil.parse2Str(cause));
throw new BizException(PcReturnCode.REMOTE_TRANSFER_ERROR);
}
};
}
}
这种就可以知道具体的错误原因 .
使用方式:
@FeignClient(value = "optimization-user", fallbackFactory = TestClient.class)
六、path
定义了当前FeignClient访问接口时的同意前缀,比如接口地址是/user/test,/user/get,如果你定义了前缀user,那么方法方法路径上直接写/test或/get就可以了。
@FeignClient(name = "optimization-user", path="user")
public interface UserRemoteClient {
@GetMapping("/get")
public User getUser(@RequestParam("id") int id);
}
七、primary
该属性与@Primary注解功能类似,默认是true,放我们feign实现了fallback进行服务兜底后,由于兜底的类是实现了@FeignClient修饰的接口们也就意味着 FeignClient有多个相同的bean在Spring容器中,当我们使用@Autowired进行注入的时候,就会出现不知道注入那个。所以这个属性就生效了,ture表示这个属性的对象是优先级高的。
八、qualifier
qualifier对应的是@Qualifier注解,使用场景跟上面的primary关系很淡,一般场景直接@Autowired直接注入就可以了。
如果我们的Feign Client有fallback实现,默认@FeignClient注解的primary=true, 意味着我们使用@Autowired注入是没有问题的,会优先注入你的Feign Client。
如果把primary设置成false了,直接用@Autowired注入的地方就会报错,不知道要注入哪个对象。
解决方案很明显,你可以将primary设置成true即可,如果由于某些特殊原因,你必须得去掉primary=true的设置,这种情况下我们怎么进行注入,我们可以配置一个qualifier,然后使用@Qualifier注解进行注入,示列如下:
@FeignClient(name = "optimization-user", path="user", qualifier="userRemoteClient")
public interface UserRemoteClient {
@GetMapping("/get")
public User getUser(@RequestParam("id") int id);
}
@Autowired
@Qualifier("userRemoteClient")
private UserRemoteClient userRemoteClient;