SpringCloud架构:8-构建独立的Feign工程及遇到的坑

346 阅读5分钟

构建独立的Feign工程

一、背景

在实际开发过程中,一般会构建一个工程,来存放整个Spring Cloud项目中的所有的Feign接口,同时也会存放Feign调用过程中的复杂类型的实体类。

这里原有一个邮件服务、一个活动申请服务,场景就是访问活动申请服务的接口,然后活动申请服务通过feign调用邮件服务。重点是独立的Feign工程,所以活动申请和邮件并无具体业务逻辑。

二、版本确认

在我自己构建工程的过程中,遇到了各种各样的坑,其中一类是由于springboot版本与SpringCloud版本的对应关系导致的,所以这里先确定下版本。

SpringBoot的版本
<parent>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

SpringCloud的版本
<dependencyManagement>
	<dependencies>
    	<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>Greenwich.SR3</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

三、建立Feign工程

  • 新建一个springboot项目,这里就叫scloud-feign,也有些人喜欢把这个工程命名为xxxx-client,不过这个不重要。
  • 因为后期这个项目是以jar包的形式引入到其他项目(服务)中的,所以删掉resource、test这些没用的文件夹。
    最终的项目结构
    在这里插入图片描述

四、Feign工程的pom

  • 另外引入openfeign依赖和lombok依赖。
  • 注意这里引入的是spring-cloud-starter-openfeign依赖而不是spring-cloud-starter-feign这里是一个坑,在一些书上,未标明springboot和springcloud的版本就直接写引入spring-cloud-starter-feign是会导致掉坑的。
  • 基于以上确定的springboot和SpringCloud的版本的情况下,请引入spring-cloud-starter-openfeign
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.scloud</groupId>
    <artifactId>feign</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>feign</name>
    <description>feign Client</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <!--Feign依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

五、在邮件服务写三个接口

  • 为了涵盖实际开发中常用的接口方式,写三个简单的发送邮件的接口。
  • 新建一个邮件实体:
@Data
public class Email {
    /** 收件人 */
    private String to;

    /** 邮件内容 */
    private String content;
	
	/** 发送结果 */
    private String rs;
}
  • 新建三个接口:
// 邮件服务
@RestController
@RequestMapping("/api/email")
public class EmailSendController {

    // GET发送邮件:参数为[信息字符串] 返回邮件实体
    @PostMapping(value = "/send/dto/re/dto")
    public Email emailPostDtoReDto(@RequestParam("to") String to, @RequestParam("content") String content){
        Email email = new Email();
        email.setTo(to);
        email.setContent(content);
        email.setRs("send ok!");
        return email;
    }

    // POST发送邮件:参数为[邮件信息实体] 返回邮件实体
    @PostMapping(value = "/send/dto/re/dto")
    public Email emailPostDtoReDto(@RequestBody Email email){
		email.setRs("send ok!");
        return email;
    }

    // POST发送邮件:参数为[邮件信息实体] 返回字符串
    @PostMapping(value = "/send/dto/re/str")
    public String emailPostDtoReStr(@RequestBody Email email){
        return "send ok";
    }
}

六、在Feign工程写Feign接口

  • 新建email包,所有指向邮件服务的feign接口都写在这个包下,这样各个服务的feign接口更清晰一些。
  • 新建一个邮件实体:
@Data
public class Email {
    /** 收件人 */
    private String to;

    /** 邮件内容 */
    private String content;
	
	/** 发送结果 */
    private String rs;
}
  • 新建一个Feign接口指向邮件服务:
    使用@FeignClient()注解标识这个feign接口指向邮件服务
    注意@FeignClient是org.springframework.cloud.openfeign.FeignClient包下的
package com.scloud.feign.email.feign;

import com.scloud.feign.email.dto.Email;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "SCLOUD-EMAIL-SERVICE")
public interface EmailFeignInterface {

    // GET发送邮件:参数为[邮件信息字符串] 返回邮件实体
    @PostMapping(value = "/api/email/send/str/re/dto")
    Email emailPostStrReDto(@RequestParam("to") String to, @RequestParam("content") String content);

    // POST发送邮件:参数为[邮件信息实体] 返回邮件实体
    @PostMapping(value = "/api/email/send/dto/re/dto")
    Email emailPostDtoReDto(@RequestBody Email email);

    // POST发送邮件:参数为[邮件信息实体] 返回字符串
    @PostMapping(value = "/api/email/send/dto/re/str")
    String emailPostDtoReStr(@RequestBody Email email);
}

在这里插入图片描述

  • 构建一下Feign工程,这样其他项目就能以jar包的形式引入Feign工程了
    在这里插入图片描述

七、在活动申请服务引入Feign工程

  • 在活动申请服务的pom中引入Feign工程
<!--feign工程-->
<dependency>
	<groupId>com.scloud</groupId>
	<artifactId>feign</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>
  • 同时也要引入open-feign依赖,:
<!--Feign依赖-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • 在活动申请服务的启动类中启用feign
    需要注意@EnableFeignClients()注解是来自包org.springframework.cloud.openfeign.EnableFeignClients
package com.scloud.activimanage;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableEurekaClient
// 有些书上是这种格式:basePackages="",有坑!记得加{}
@EnableFeignClients(basePackages = {"com.scloud.feign.*"})	// 启用Feign
@SpringBootApplication
public class ActivimanageApplication {
    public static void main(String[] args) {
        SpringApplication.run(ActivimanageApplication.class, args);
    }
}

八、在活动申请服务中使用Feign访问邮件服务

引入Feign工程中的指向邮件服务的feign接口,即可使用。当然如果有其他服务也需要调用邮件服务,那就同样引入Feign工程中的接口就能用了。

@RestController
@RequestMapping("/api/activity")
public class ActivityApplyController {

    @Autowired
    private EmailFeignInterface emailFeignInterface;

    // 申请活动
    @GetMapping(value = "/apply")
    public String applyActivity(){
        System.out.println("申请活动");

        // email服务feign接口1
        Email email1 = emailFeignInterface.emailPostStrReDto("2213@1qq.com", "apply activi");
        System.out.println("feign接口1:"+email1.getRs());

        // email服务feign接口2
        Email email2 = new Email();
        email2.setTo("2213@1qq.com");
        email2.setContent("apply activi");
        String rs = emailFeignInterface.emailPostDtoReStr(email2);
        System.out.println("feign接口2:"+rs);

        // email服务feign接口3
        Email email3 = emailFeignInterface.emailPostDtoReDto(email2);
        System.out.println("feign接口3:"+email1.getRs());

       return "ok";
    }
}

九、结果

访问一下活动申请服务的接口,测试一下,发现能够成功通过feign接口访问到邮件服务。
在这里插入图片描述

十、避免掉坑

  • 先确认好springboot和SpringCloud的版本号
  • 在springboot 2.1.7.RELEASE版本、SpringCloud Greenwich.SR3版本的前提下,Feign工程和需要使用feign调用的工程请引入spring-cloud-starter-openfeign依赖。
  • 早些版本是引入的spring-cloud-starter-feign依赖,可能有些书上未注意到版本的差异,比如用以上的2.1.7.RELEASE和Greenwich.SR3版本,再引入spring-cloud-starter-feign就会出现访问不到活动申请接口的诡异的坑。