天天看点

Feign的基础使用1. Feign的入门使用2. OpenFeign简介1. 创建demo-feign工程2. 创建demo-remote-client工程3. @FeignClient注解4. @EnableFeignClient注解

文章目录

  • 1. Feign的入门使用
    • 1. 导入依赖
    • 2. 示例
    • 3. Feign的处理流程图
  • 2. OpenFeign简介
  • 1. 创建demo-feign工程
    • 1.导入依赖
    • 2. 定义POJO
    • 3. 定义FeignClient接口
      • GithubFeignClient
      • RemoteFeignClient
    • 4. 使用FeiClient调用远程的接口IndexController
    • 5. 开启FeignClient接口扫描
  • 2. 创建demo-remote-client工程
    • 1. 导入依赖
    • 2. 定义POJO
    • 3. 定义供调用的接口RemoteController
    • 4. 启动类
  • 3. @FeignClient注解
    • @FeignClient注解说明
  • 4. @EnableFeignClient注解
    • @EnableFeignClient注解说明

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的处理流程图

Feign的基础使用1. Feign的入门使用2. OpenFeign简介1. 创建demo-feign工程2. 创建demo-remote-client工程3. @FeignClient注解4. @EnableFeignClient注解

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的类必须实现[email protected]标注的接口,无法知道熔断的异常信息
	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 {};

}
           

继续阅读