文章目录
1. Feign的入门使用
Feign的本质就是通过传入的接口类,构造该接口的动态代理对象,发起Http请求,获取响应数据,并帮我们完成数据封装并返回
。
1. 导入依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>9.5.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
<version>9.5.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
2. 示例
interface GitHub {
// 【效果与访问该url一致:https://api.github.com/repos/OpenFeign/feign/contributors】
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
@RequestLine("POST /repos/{owner}/{repo}/issues")
void createIssue(Issue issue, @Param("owner") String owner, @Param("repo") String repo);
}
public static class Contributor {
String login;
int contributions;
}
public static class Issue {
String title;
String body;
List<String> assignees;
int milestone;
List<String> labels;
}
public class MyApp {
public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
// Fetch and print a list of the contributors to this library.
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
}
3. Feign的处理流程图
2. OpenFeign简介
SpringCloud在Feign的基础上,增加了对SpringMvc注解的支持
,让使用Feign更加的方便。
1. 创建demo-feign工程
1.导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zzhua</groupId>
<artifactId>demo-feign</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-Feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
</project>
2. 定义POJO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address {
private String country;
private String province;
}
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
private Address address;
}
@Data
public class Contributor {
String login;
int contributions;
}
3. 定义FeignClient接口
GithubFeignClient
@FeignClient(name = "github",url = "https://api.github.com/repos")
public interface GithubFeignClient {
// 访问效果同:https://api.github.com/repos/OpenFeign/feign/contributors
@GetMapping("{owner}/{repo}/contributors")
List<Contributor> contributors(@PathVariable("owner") String owner, @PathVariable("repo") String repo);
}
RemoteFeignClient
@FeignClient(name = "remote",
url = "http://localhost:8081",
path = "remote")
public interface RemoteFeignClient {
@GetMapping("findPerson") // 多于1个参数,则必须写@RequestParam注解(并且必须写value)
Person findPerson(@RequestParam(name = "pName") String name,
@RequestParam(name = "pAge") Integer age);
@GetMapping("getPerson") // @RequestParam后面是自定义参数类型将不会封装到接口的方法参数中
Person getPerson(@RequestParam("person") Person person);
@GetMapping("getPerson1") // feign将会把url拼接成url?name=xx&age=yy&address=zz(address字符串形式)
// 这个address将会导致接口那边在获取address时,不能正常封装成Address对象而导致报错
Person getPerson1(@RequestParam(name = "name") String name,
@RequestParam(name = "age") Integer age
,@RequestParam(name = "address") Address address);
@PostMapping("getPerson2") // 可以使用Map封装(远程接口使用@RequestBody Map来接收(address属性能正常接收到))
Person getPerson2(Map<String,Object> map);
@PostMapping("addPerson") // 不能使用超过1个@RequestBody
Person addPerson(@RequestBody Person person,@RequestParam("pAge") Integer age);
@PostMapping("addPerson2") // @RequestParam后面是自定义参数Address将不会封装到接口的方法参数中
Person addPerson2(@RequestBody Person person,@RequestParam("addr") Address addr);
@PostMapping("checkPerson") // 多于1个参数,则必须写@RequestParam注解(并且必须写value)
// feign拼接url?ids=1%2C2%2C3 (%2C,即逗号)
Person checkPerson(@RequestBody Person person, @RequestParam("ids") Integer[] ids);
@PostMapping("checkPerson2") // 多于1个参数,则必须写@RequestParam注解(并且必须写value)
// feign拼接url??ids=1&ids=2&ids=3
Person checkPerson2(@RequestBody Person person, @RequestParam("ids") List<Integer> ids);
}
4. 使用FeiClient调用远程的接口IndexController
@RestController
public class IndexController {
@Autowired
private GithubFeignClient consumerFeignClient;
@Autowired
private RemoteFeignClient remoteFeignClient;
@RequestMapping("github/contributors")
public List<Contributor> contributors() {
List<Contributor> contributors = consumerFeignClient.contributors("OpenFeign", "feign");
return contributors;
}
@GetMapping("findPerson")
public Person findPerson() {
Person person = remoteFeignClient.findPerson("zzhua",26);
return person;
}
@GetMapping("getPerson")
public Person getPerson() {
Person person = remoteFeignClient.getPerson(new Person("zzhua", 26, new Address("CN", "HN")));
return person;
}
@GetMapping("getPerson1")
public Person getPerson1() {
Person person = remoteFeignClient.getPerson1("zzhua", 26, new Address("CN", "HN"));
return person;
}
@GetMapping("getPerson2")
public Person getPerson2() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "zzhua");
map.put("age", 26);
map.put("address", new Address("CN", "HN"));
Person person = remoteFeignClient.getPerson2(map);
return person;
}
@GetMapping("addPerson")
public Person addPerson() {
Person person = remoteFeignClient.addPerson(new Person("zzhua", 26, new Address("CN", "HN")), 2);
return person;
}
@GetMapping("addPerson2")
public Person addPerson2() {
Person person = remoteFeignClient.addPerson2(new Person("zzhua", 26, new Address("CN", "HN")),
new Address("CN2", "HN2"));
return person;
}
@GetMapping("checkPerson")
public Person checkPerson() {
return remoteFeignClient.checkPerson(new Person("zzhua", 26, new Address("CN", "HN")),
new Integer[]{1,2,3});
}
@GetMapping("checkPerson2")
public Person checkPerson2() {
return remoteFeignClient.checkPerson2(new Person("zzhua", 26, new Address("CN", "HN")),
Arrays.asList(1,2,3));
}
}
5. 开启FeignClient接口扫描
@EnableFeignClients("com.zzhua.feign") // 如果不加具体的包名,将会扫描该注解所标注的类所在包及其子包下的FeignClient接口
@SpringBootApplication
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
2. 创建demo-remote-client工程
1. 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
2. 定义POJO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address {
private String country;
private String province;
}
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
private Address address;
}
3. 定义供调用的接口RemoteController
@RestController
@RequestMapping("remote")
public class RemoteController {
@GetMapping("findPerson")
Person findPerson(String pName, Integer pAge) {
return new Person(pName, pAge, new Address("CN", "HN"));
}
@GetMapping("getPerson")
Person getPerson(Person person) {
System.out.println(person);
return person;
}
@PostMapping("getPerson2")
Person getPerson2(@RequestBody Map<String,Object> cMap) {
System.out.println(cMap);
return new Person();
}
@PostMapping("addPerson")
Person addPerson(@RequestBody Person person,Integer pAge) {
System.out.println(person);
System.out.println(pAge);
if (person.getAge() != null) {
person.setAge( person.getAge() + 1);
}
return person;
}
@PostMapping("addPerson2")
Person addPerson2(@RequestBody Person person,Address addr) {
System.out.println(person);
System.out.println(addr);
if (person != null) {
person.setAddress(addr);
}
return person;
}
@PostMapping("checkPerson")
Person checkPerson(@RequestBody Person person, Integer[] ids) {
System.out.println(person);
System.out.println(StringUtils.arrayToCommaDelimitedString(ids));
return person;
}
@PostMapping("checkPerson2") // List<Integer> ids必须要带@RequestParam注解才能接收到
Person checkPerson2(@RequestBody Person person, @RequestParam("ids") List<Integer> ids) {
System.out.println(person);
System.out.println(ids);
return person;
}
}
4. 启动类
@SpringBootApplication
public class RemoteApplication {
public static void main(String[] args) {
SpringApplication.run(RemoteApplication.class, args);
}
}
3. @FeignClient注解
@FeignClient注解说明
@FeignClient注解主要被@Target({ElementType.TYPE})修饰,表示该注解主要使用在接口上
。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
// 同name, 用来指定FeignClient的名称,如果没有配置url,name就`作为微服务的名称`,用于服务发现
@AliasFor("name")
String value() default "";
// serviceId已经废弃了,直接使用name即可
@Deprecated
String serviceId() default "";
// 如果指定,则会被用来作为feignClient的bean的名称,否则默认会取name(同value)属性
String contextId() default "";
// 同name, 用来指定FeignClient的名称,如果没有配置url,name就`作为微服务的名称`,用于服务发现
@AliasFor("value")
String name() default "";
// url用于配置指定服务的地址,相当于直接请求这个服务,不经过Ribbon的服务选择。像调试等场景可以使用
String url() default "";
// 当调用请求发生404错误时,decode404的值为true,那么会执行decoder解码,否则抛出异常。
boolean decode404() default false;
// 配置Feign的配置类,在配置类中可以自定义Feign的Encoder、Decoder、LogLevel、Contract等
// 默认使用的配置类是: FeignClientsConfiguration
Class<?>[] configuration() default {};
// 定义容错的处理类(定义为bean),也就是回退逻辑,fallback的类必须实现F@eignClient标注的接口,无法知道熔断的异常信息
Class<?> fallback() default void.class;
// 也是容错的处理(定义为bean,实现FallbackFactory接口),可以知道熔断的异常信息
Class<?> fallbackFactory() default void.class;
// path定义当前FeignClient访问接口时的统一前缀,比如接口地址是/user/get, 如果你定义了前缀是user, 那么具体方法上的路径就只需要写/get 即可。
String path() default "";
// primary对应的是@Primary注解,默认为true,官方这样设置也是有原因的。
// 当我们的Feign实现了fallback后,也就意味着Feign Client有多个相同的Bean在Spring容器中,
// 当我们在使用@Autowired进行注入的时候,不知道注入哪个,所以我们需要设置一个优先级高的,@Primary注解就是干这件事情的。
boolean primary() default true;
// 如果配置了qualifier优先用qualifier作为别名,qualifier对应的是@Qualifier注解
// 当我们在容器中定义了fallback的bean,而feignClient的bean的primary属性又被设置了false,
// 那么可以使用这个属性设置,而在注入端使用@Quarlifier("指定这个属性值")
String qualifier() default "";
}
4. @EnableFeignClient注解
扫描
使用@FeignClient
注解所标注的feign客户端接口
@EnableFeignClient注解说明
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
// 同basePackages(互斥)
String[] value() default {};
// 扫描组件所在的基础包(互斥)
String[] basePackages() default {};
// 扫描 所指定类所在的包下的所有类(可以在某个包下,专门定义一个的类用来标记要扫描这个包)
Class<?>[] basePackageClasses() default {};
// 为所有的feign客户端(注意是所有哦)指定自定义配置(默认使用FeignClientsConfiguration),
// 在配置类中可以自定义Feign的Encoder、Decoder、LogLevel、Contract等
Class<?>[] defaultConfiguration() default {};
// 指定使用了@FeignClient注解的类(如果指定了该属性,将关闭类路径扫描)
Class<?>[] clients() default {};
}