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.yml2. 父工程 pom.xml(根目录)
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="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 version="1.0" encoding="UTF-8"?><projectxmlns="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
packagecom.demo.common.dto;importlombok.Data;@Data// 自动生成 getter/setter/toStringpublicclassUser{privateLongid;privateStringname;privateStringemail;}common/src/main/java/com/demo/common/api/UserApi.java
packagecom.demo.common.api;importcom.demo.common.dto.User;importorg.springframework.cloud.openfeign.FeignClient;importorg.springframework.web.bind.annotation.GetMapping;importorg.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")publicinterfaceUserApi{@GetMapping("/users/{id}")UsergetUserById(@PathVariable("id")Longid);}4. service-b 模块(服务提供方,实现接口)
service-b/pom.xml
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="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
server:port:8081spring:application:name:service-b# 必须和 @FeignClient(name = "service-b") 保持一致service-b/src/main/java/com/demo/serviceb/ServiceBApplication.java
packagecom.demo.serviceb;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication// 重点注意:B 服务是提供方,这里绝对不能加 @EnableFeignClientspublicclassServiceBApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ServiceBApplication.class,args);}}service-b/src/main/java/com/demo/serviceb/controller/UserController.java
packagecom.demo.serviceb.controller;importcom.demo.common.api.UserApi;importcom.demo.common.dto.User;importorg.springframework.web.bind.annotation.RestController;@RestController// 关键点:实现 common 中定义的接口,自动继承 @GetMapping 路径publicclassUserControllerimplementsUserApi{@OverridepublicUsergetUserById(Longid){// 模拟数据库查询,返回假数据Useruser=newUser();user.setId(id);user.setName("张三(来自B服务)");user.setEmail("zhangsan@service-b.com");returnuser;}}5. service-a 模块(服务调用方,使用 Feign)
service-a/pom.xml
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="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
server:port:8080spring:application:name:service-aservice-a/src/main/java/com/demo/servicea/ServiceAApplication.java
packagecom.demo.servicea;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication// 必须加此注解,并指定扫描 common 模块中 Feign 接口所在的包@EnableFeignClients(basePackages="com.demo.common.api")publicclassServiceAApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ServiceAApplication.class,args);}}service-a/src/main/java/com/demo/servicea/controller/OrderController.java
packagecom.demo.servicea.controller;importcom.demo.common.api.UserApi;importcom.demo.common.dto.User;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.RestController;@RestControllerpublicclassOrderController{@AutowiredprivateUserApiuserApi;// 直接注入 common 里的接口@GetMapping("/order/{id}/user")publicUsergetOrderUser(@PathVariable("id")Longid){// 看上去像本地调用,实际上发起了 HTTP 请求给 service-breturnuserApi.getUserById(id);}}6. 运行测试
- 先启动 service-b(右键运行
ServiceBApplication.main),控制台看到Started ServiceBApplication on port 8081。 - 再启动 service-a(右键运行
ServiceAApplication.main),控制台看到Started ServiceAApplication on port 8080。 - 打开浏览器或 Postman,访问:
http://localhost:8080/order/123/user
返回结果(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)。 |