关于微服务基本框架搭建--fegin最小使用demo

1. 整体目录结构

复制代码
microservice-demo(父工程)
├── pom.xml(父POM,管理版本)
├── common(公共模块)
│   ├── pom.xml
│   └── src/main/java/com/demo/common/
│       ├── dto/User.java
│       └── api/UserApi.java
├── service-b(服务提供方,端口8081)
│   ├── pom.xml
│   ├── src/main/java/com/demo/serviceb/
│   │   ├── ServiceBApplication.java
│   │   └── controller/UserController.java
│   └── src/main/resources/application.yml
└── service-a(服务调用方,端口8080)
    ├── pom.xml
    ├── src/main/java/com/demo/servicea/
    │   ├── ServiceAApplication.java
    │   └── controller/OrderController.java
    └── src/main/resources/application.yml

2. 父工程 pom.xml(根目录)

xml 复制代码
<?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.demo</groupId>
    <artifactId>microservice-demo</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <!-- 引入 Spring Boot 父依赖,统一版本 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.14</version>
        <relativePath/>
    </parent>

    <!-- 管理 Spring Cloud 版本 -->
    <properties>
        <java.version>8</java.version>
        <spring-cloud.version>2021.0.8</spring-cloud.version>
    </properties>

    <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>

    <modules>
        <module>common</module>
        <module>service-b</module>
        <module>service-a</module>
    </modules>

</project>

3. common 模块(定义 API 契约)

common/pom.xml

xml 复制代码
<?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">
    <parent>
        <artifactId>microservice-demo</artifactId>
        <groupId>com.demo</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>common</artifactId>

    <dependencies>
        <!-- 必备:包含 @FeignClient 注解 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- 必备:包含 @GetMapping, @PathVariable 等注解 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 为了少写 getter/setter,引入 Lombok(可选) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

</project>

common/src/main/java/com/demo/common/dto/User.java

java 复制代码
package com.demo.common.dto;

import lombok.Data;

@Data  // 自动生成 getter/setter/toString
public class User {
    private Long id;
    private String name;
    private String email;
}

common/src/main/java/com/demo/common/api/UserApi.java

java 复制代码
package com.demo.common.api;

import com.demo.common.dto.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

// name = "service-b" 对应 B 服务的 spring.application.name
// path = "/api" 统一前缀,方便 B 服务统一管理
// 这里暂时写死 url,方便不接注册中心直接运行(接入注册中心时删掉 url 即可)
@FeignClient(name = "service-b", url = "http://localhost:8081", path = "/api")
public interface UserApi {

    @GetMapping("/users/{id}")
    User getUserById(@PathVariable("id") Long id);
}

4. service-b 模块(服务提供方,实现接口)

service-b/pom.xml

xml 复制代码
<?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">
    <parent>
        <artifactId>microservice-demo</artifactId>
        <groupId>com.demo</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>service-b</artifactId>

    <dependencies>
        <!-- 依赖 common 模块 -->
        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>common</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!-- web 容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

service-b/src/main/resources/application.yml

yaml 复制代码
server:
  port: 8081

spring:
  application:
    name: service-b   # 必须和 @FeignClient(name = "service-b") 保持一致

service-b/src/main/java/com/demo/serviceb/ServiceBApplication.java

java 复制代码
package com.demo.serviceb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
// 重点注意:B 服务是提供方,这里绝对不能加 @EnableFeignClients
public class ServiceBApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceBApplication.class, args);
    }
}

service-b/src/main/java/com/demo/serviceb/controller/UserController.java

java 复制代码
package com.demo.serviceb.controller;

import com.demo.common.api.UserApi;
import com.demo.common.dto.User;
import org.springframework.web.bind.annotation.RestController;

@RestController
// 关键点:实现 common 中定义的接口,自动继承 @GetMapping 路径
public class UserController implements UserApi {

    @Override
    public User getUserById(Long id) {
        // 模拟数据库查询,返回假数据
        User user = new User();
        user.setId(id);
        user.setName("张三(来自B服务)");
        user.setEmail("zhangsan@service-b.com");
        return user;
    }
}

5. service-a 模块(服务调用方,使用 Feign)

service-a/pom.xml

xml 复制代码
<?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">
    <parent>
        <artifactId>microservice-demo</artifactId>
        <groupId>com.demo</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>service-a</artifactId>

    <dependencies>
        <!-- 依赖 common 模块 -->
        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>common</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

service-a/src/main/resources/application.yml

yaml 复制代码
server:
  port: 8080

spring:
  application:
    name: service-a

service-a/src/main/java/com/demo/servicea/ServiceAApplication.java

java 复制代码
package com.demo.servicea;

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

@SpringBootApplication
// 必须加此注解,并指定扫描 common 模块中 Feign 接口所在的包
@EnableFeignClients(basePackages = "com.demo.common.api")
public class ServiceAApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceAApplication.class, args);
    }
}

service-a/src/main/java/com/demo/servicea/controller/OrderController.java

java 复制代码
package com.demo.servicea.controller;

import com.demo.common.api.UserApi;
import com.demo.common.dto.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @Autowired
    private UserApi userApi;  // 直接注入 common 里的接口

    @GetMapping("/order/{id}/user")
    public User getOrderUser(@PathVariable("id") Long id) {
        // 看上去像本地调用,实际上发起了 HTTP 请求给 service-b
        return userApi.getUserById(id);
    }
}

6. 运行测试

  1. 先启动 service-b (右键运行 ServiceBApplication.main),控制台看到 Started ServiceBApplication on port 8081
  2. 再启动 service-a (右键运行 ServiceAApplication.main),控制台看到 Started ServiceAApplication on port 8080
  3. 打开浏览器或 Postman,访问:http://localhost:8080/order/123/user

返回结果(JSON):

json 复制代码
{
    "id": 123,
    "name": "张三(来自B服务)",
    "email": "zhangsan@service-b.com"
}

7. 特别提示(再强调一遍)

检查项 说明
B 服务启动类 绝对不要@EnableFeignClients,否则它会尝试把自己注册成 Feign 客户端而报错。
A 服务启动类 必须@EnableFeignClients(basePackages = "com.demo.common.api"),否则 UserApi 无法被注入(报 NoSuchBeanDefinitionException)。
common 依赖 如果 common 模块打包报错,记得先 mvn clean install 安装到本地仓库,再启动 A 和 B。
路径一致性 B 服务的 UserController 由于实现了接口,不要再写 @RequestMapping 在类上,否则路径会变成双份(如 /api/api/users)。
相关推荐
梦想的旅途21 小时前
企业微信API 实现企业微信消息的自动发送?
java·服务器·前端
大不点wow1 小时前
为什么 Spring 更推荐构造器注入,而不是字段 `@Autowired`
java·spring·intellij-idea
用户298698530141 小时前
Java 创建 CSV 文件的三种实用方法:从零构建、数组写入与 Excel 转换
java·后端·excel
Cry丶2 小时前
一次 FTP 上传引发 504 Gateway Timeout 的排查复盘
java·nginx·ftp·openresty·被动模式·504 gateway·生产问题排查
码农进化录2 小时前
Java 程序员的 AI 进化论 | AI 辅助 CI/CD,让 AI 帮你做代码审查和部署决策
java·spring boot·openai
zyplayer-doc2 小时前
企业文档的进化:网盘和在线文档的下一个升级方向—知识库系统
java·大数据·数据库·人工智能·ocr
吃饱了得干活2 小时前
从零设计一个高并发点赞系统:缓存+MQ异步持久化实战
java·架构
奇牙coding2 小时前
gpt-5.6-luna 调用报 400 bad_request 怎么办?同样的 messages 在 gpt-5.5 没问题——两处校验差异和修复写法
java·windows·gpt·ai
凤山老林2 小时前
从“无法创建本地线程“异常说起:深度解析线程栈与JVM线程优化指南
java·开发语言·jvm