使用Feign代替RestTemplate完成服务调用

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

Springboot集成Feign遇到了很多问题,配置成功后决定记录一下,方便后续查看,如果同时能帮助到有需要的人,我将不胜欣喜!

提示:以下是本篇文章正文内容,下面案例可供参考

一、版本

Springboot 3.3.2
Spring Cloud Alibaba 2023.0.1.0
Feign 4.1.3

二、工程创建

1. 创建工程文件

初始配置如下:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/637fbcb9989a40b992878cccfa80cdb9.png

2. 添加依赖

在项目的pom.xml文件中添加 Spring Cloud Alibaba 的依赖:

<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>com.alibaba.cloud</groupId>
				<artifactId>spring-cloud-alibaba-dependencies</artifactId>
				<version>2023.0.1.0</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

3. 创建微服务

按照图片一步步创建两个微服务 order-service 和 user-service 即可
(1)创建 Module

在这里插入图片描述

(2)配置 Module

在这里插入图片描述
(3)最终的目录结构
在这里插入图片描述
(4)pom.xml 内容

  • CloudNacos04 的 pom.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<packaging>pom</packaging>
	<modules>
		<module>order-service</module>
		<module>user-service</module>
	</modules>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.abc</groupId>
	<artifactId>CloudNacos04</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>CloudNacos04</name>
	<description>Demo project for Spring Boot</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>3.0.3</version>
		</dependency>

		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter-test</artifactId>
			<version>3.0.3</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>com.alibaba.cloud</groupId>
				<artifactId>spring-cloud-alibaba-dependencies</artifactId>
				<version>2023.0.1.0</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

  • order-service 的 pom.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>CloudNacos04</artifactId>
        <groupId>com.abc</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>order-service</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <!--Nacos服务发现依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- SpringCloud Loadbalancer -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
            <version>4.1.3</version>
        </dependency>
    </dependencies>
</project>
  • user-service 的 pom.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>CloudNacos04</artifactId>
        <groupId>com.abc</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>user-service</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

</project>

记得配置完pom.xml文件一定要点右上角蓝色图标的 ”Load Maven change” 导入依赖

(5)配置application.yaml

order-service 和 user-service 两个微服务都需要配置yaml文件,如何配置参考图片(这里就是最基本的配置,不涉及nacos的共享配置)。

在这里插入图片描述
(6)具体功能实现的编码

目录结构如下:

在这里插入图片描述

  • OrderMapper.java
package com.qmm.mapper;

import com.qmm.pojo.Order;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface OrderMapper {
    @Select("select * from tb_order where id = #{id}")
    public Order findByID(int id);
}

  • OrderServiceImpl.java
package com.qmm.service.impl;

import com.qmm.mapper.OrderMapper;
import com.qmm.pojo.Order;
import com.qmm.pojo.User;
import com.qmm.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    OrderMapper orderMapper;

    @Autowired
    RestTemplate restTemplate;

    @Override
    public Order queryOrderById(int orderId) {
        // 1. 查询订单
        Order order = orderMapper.findByID(orderId);

        // 2. 利用RestTemplate发起http请求
        String url = "http://localhost:8082/user/" + order.getUserId();
        User user = restTemplate.getForObject(url, User.class);

        // 3. 封装user到order
        order.setUser(user);

        return order;
    }
}
  • OrderController.java
package com.qmm.controller;

import com.qmm.pojo.Order;
import com.qmm.service.OrderService;
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
    OrderService orderService;

    @GetMapping("/order/{id}")
    public Order queryById(@PathVariable int id){
        return orderService.queryOrderById(id);
    }
}
  • order-service 的启动类 OrderApplication.java
package com.qmm;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

  • UserMapper.java
package com.qmm.mapper;

import com.qmm.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper {
    @Select("select * from tb_user where id = #{id}")
    public User findById(int id);
}
  • UserServiceImpl.java
package com.qmm.service.impl;

import com.qmm.mapper.UserMapper;
import com.qmm.pojo.User;
import com.qmm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;

    @Override
    public User findById(int id) {
        return userMapper.findById(id);
    }
}
  • UserController.java
package com.qmm.controller;

import com.qmm.pojo.User;
import com.qmm.service.UserService;
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 UserController {
    @Autowired
    UserService userService;

    @GetMapping("/user/{id}")
    public User findUserById(@PathVariable int id) {
        return userService.findById(id);
    }
}
  • user-service 的启动类 UserApplication.java
package com.qmm;

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

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

  • 实体类 User.java
package com.qmm.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String address;
}
  • 实体类 Order.java
package com.qmm.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
   private Integer id;
   private Integer price;
   private String name;
   private Integer num;
   private Integer userId;
   private User user;
}

4. 启动微服务

  • 单机模式启动nacos,命令如下:
    startup.cmd -m standalone
  • 运行启动类 UserApplicaiton.java 和 OrderApplication.java
  • 地址栏输入 localhost:8848/nacos
  • 若在服务列表看到两个服务的信息,则说明注册成功
    在这里插入图片描述
  • 服务调用
    在这里插入图片描述

5. Feign远程调用

使用Feign代替ResTemplate发送http请求

5.1 引入依赖

在order-service的pom.xml中添加如下依赖:

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
      <version>4.1.3</version>
</dependency>

一定要注意Spring Cloud Alibaba 与 Feign 的版本匹配

5.2 添加注解

在order-service的启动类添加注解 @EnableFeignClients 开启Feign功能
在这里插入图片描述

5.3 编写Feign客户端

package com.qmm.client;

import com.qmm.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

// 声明远程调用信息
@FeignClient("userservice") //指定服务名称
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable int id);
}

这个客户端主要是基于SpringMVC的注解来声明远程调用的信息,比如:

  • 服务名称:userservice
  • 请求方式:GET
  • 请求路径:/user/{id}
  • 请求参数:int id
  • 返回值类型:User

这样,Feign就可以帮助我们发送http请求,无需自己使用RestTemplate来发送了。

5.4 利用Feign发送http请求

修改OrderServiceImpl.java:

package com.qmm.service.impl;

import com.qmm.client.UserClient;
import com.qmm.mapper.OrderMapper;
import com.qmm.pojo.Order;
import com.qmm.pojo.User;
import com.qmm.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    OrderMapper orderMapper;

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    UserClient userClient;

    @Override
    public Order queryOrderById(int orderId) {
        // 1. 查询订单
        Order order = orderMapper.findByID(orderId);

//        // 2. 利用RestTemplate发起http请求
//        String url = "http://userservice/user/" + order.getUserId();
//        User user = restTemplate.getForObject(url, User.class);

        // 2. 利用Feign发起http请求,查询用户
        User user = userClient.findById(order.getUserId());

        // 3. 封装user到order
        order.setUser(user);

        return order;
    }
}

5.5 启动微服务,完成自测即可

总结

本文只是利用Nacos和Feign完成服务注册与远程调用的基本配置,就上述这样的一个配置过程我实践了很多次,恐怕现在还是不能一次调通,这期间遇到的问题包括但不限于:

  • Spring Cloud Alibaba 与 Feign 版本不匹配
  • pom.xml 依赖导入后,忘记刷新"Load Maven changes"

在写这篇文章的时候,我又发现了一个新问题且至今不知道原因,如下图,不添加注解@LoadBalanced会报错
在这里插入图片描述
报错如下:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://userservice/user/3": userservice] with root 

就写到这吧,如有问题或者建议,欢迎提出!

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值