1. 程式人生 > >Spring Cloud Feign(宣告式服務呼叫)(1)

Spring Cloud Feign(宣告式服務呼叫)(1)

Spring Cloud Feign它基於Netflix Feign實現,整合了Spring Cloud Ribbon與Spring Cloud Hystrix,除了提供這兩者的強大功能,它還提供了一種宣告式的Web服務客戶端定義方式。

1.下面首先建立一個Spring Boot基礎工程取名為feign-consumer並在其pom.xml檔案中加入spring-cloud-starter-eureka和spring-cloud-starter-feign依賴。

<?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.example</groupId>
    <artifactId>feign-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>feign-consumer</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

2.在主類上新增@EnableFeignClients註解開啟Spring Cloud Feign的支援功能:

@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class FeignConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignConsumerApplication.class, args);
    }
}

3.定義HelloService介面,通過@FeignClient註解指定服務名來繫結服務,然後使用Spring MVC的註解來繫結具體該服務提供的REST介面。

@FeignClient("hello-service")//用於繫結名為hello-service服務
public interface HelloService {

    @RequestMapping("/hello")
    public String hello(String id);

}

4.最後建立一個控制器來實現對Feign客戶端的呼叫。使用@AutoWired直接注入HelloService例項,並在該控制器中直接呼叫hello-service服務發起的/hello介面的呼叫。

@RestController
public class ConsumerController {

    @Autowired
    HelloService helloService;

    @GetMapping("/feign-consumer")
    public String helloConsumer(String id){
        return helloService.hello(id);
    }
}

5.修改application.properties檔案:

spring.application.name=feign-consumer
server.port=9001

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

不過此時是沒有引數繫結能力的所以沒有列印id。

引數繫結

首先擴充套件一下服務提供者(hello-service)。

@RestController
public class HelloController {

    private final Logger logger=Logger.getLogger(getClass());

       @RequestMapping("/hello")
    public String index() throws InterruptedException {
//        int sleepTime=new Random().nextInt(3000);
//        logger.info("sleep:"+sleepTime);
//        Thread.sleep(sleepTime);
        logger.info(new Date());
        return "Hello"+new Date()+"---"+new Random().nextInt();
    }


    @RequestMapping(value = "/hello1", method = RequestMethod.GET)
    public String hello1(@RequestParam String name) {
        return "hello " + name + "!";
    }

    @RequestMapping(value = "/hello2", method = RequestMethod.GET)
    public Map<String,Object> hello2(@RequestHeader String name, @RequestHeader String author, @RequestHeader Integer price) throws UnsupportedEncodingException {
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("name",URLDecoder.decode(name));
        map.put("author",URLDecoder.decode(author));
        return map;
    }

    @RequestMapping(value = "/hello3", method = RequestMethod.POST)
    public String hello3(@RequestBody Map<String,Object> book) {
        return "書名為:" + book.get("name") + ";作者為:" + book.get("author");
    }

    @RequestMapping(value = "/hello4", method = RequestMethod.POST)
    public String hello4(@RequestBody User user) {
        return "使用者名稱:" + user.getName() + ";年齡為:" + user.getAge();
    }
}

User類:


public class User {

    private String name;
    private Integer age;

    public User(){}

    public User(String name, Integer age){
        this.name=name;
        this.age=age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

然後在feign-consumer中也建立逾期一樣的User類。

然後再HelloService介面中新增上述介面的繫結:

@FeignClient("hello-service")//用於繫結名為hello-service服務
public interface HelloService {

    @RequestMapping("/hello")
    public String hello();

    @RequestMapping(value = "/hello1", method = RequestMethod.GET)
    String hello(@RequestParam("name") String name);

    @RequestMapping(value = "/hello2", method = RequestMethod.GET)
    Map<String,Object> hello(@RequestHeader("name") String name, @RequestHeader("author") String author, @RequestHeader("price") Integer price);

    @RequestMapping(value = "/hello3", method = RequestMethod.POST)
    String hello(@RequestBody Map<String,Object> book);

    @RequestMapping(value = "/hello4", method = RequestMethod.POST)
    String hello(@RequestBody User user);
}

這裡需要注意在繫結引數時,@RequestParam,@RequestHeader等可以指定引數名稱的註解的value引數不能少。在Feign中繫結引數必須通過value來指定具體的引數名不然會丟擲IlleglStateException異常,value屬性不能為空。

最後控制器新增上述介面:

@RestController
public class ConsumerController {

    @Autowired
    HelloService helloService;

    @GetMapping("/feign-consumer")
    public String helloConsumer(){
        return helloService.hello();
    }

    @RequestMapping("/hello1")
    public String hello1() {
        return helloService.hello("張三");
    }

    @RequestMapping(value = "/hello2")
    public Map<String,Object> hello2() throws UnsupportedEncodingException {
        Map<String,Object> book = helloService.hello(URLEncoder.encode("三國演義","UTF-8"), URLEncoder.encode("羅貫中","UTF-8"), 33);
        System.out.println(book);
        return book;
    }

    @RequestMapping("/hello3")
    public String hello3() {
        Map<String,Object> book = new HashMap<String,Object>();
        book.put("name","三國演義");
        book.put("author","羅貫中");
        return helloService.hello(book);
    }

    @RequestMapping("/hello4")
    public String hello4() {
        User user=new User("劉德華",99);
        return helloService.hello(user);
    }

}