目的:在本地搭建一个简单的openfeign使用场景,测试查询、新增等功能
平台:win10
技术栈:Springboot, SpringCloud, Nacos, Mybatis, MySql, Logback
框架作用说明:
- Nacos用于服务注册,将provider应用注册到Nacos,
- 客户端订阅该服务,然后用openfeign调用provider提供的rest接口
- 数据库采用mysql,mybatis提供orm功能
- 日志用logback
项目架构如下:
总共启动了4个应用:
nacos server, feigndemo (provider), feignclientdemo(client), mysql server
Nacos server采用单机模式启动:
startup.cmd -m standalone
MySQL:
创建数据库、用户,给用户授权,然后建表
create database feigndemo default character set utf8mb4;
use feigndemo;
create user 'feigndemo'@'localhost' identified by '123456';
-- grant privileges
Grant SELECT, insert, create,delete,update,alter,drop on feigndemo.* to 'feigndemo'@'localhost' with grant option;
create table product (
id int(16),
name varchar(64),
price double(10,2),
delete_flag tinyint(1) not null default 0 comment '0:未删除,1:已删除',
create_time datetime not null default CURRENT_TIMESTAMP comment '创建时间',
update_time datetime not null default CURRENT_TIMESTAMP comment '更新时间'
) ENGINE=InnoDB DEFAULT charset=utf8mb4;
feigndemo主要源码
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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>feigndemo</artifactId>
<version>1.0-SNAPSHOT</version>
<name>feigndemo</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.7.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件:
application.yml
server:
port: 8088
spring:
datasource:
url: jdbc:mysql://localhost:3306/feigndemo?characterEncoding=UTF-8
username: feigndemo
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
application:
name: feigndemo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: b2b3c55a-abe5-47a4-a47e-03c57138d1d7
mybatis:
mapper-locations: classpath*:mapper/*.xml
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<contextName>${APP_NAME}</contextName>
<springProperty name="APP_NAME" scope="context" source="spring.application.name"/>
<springProperty name="LOG_FILE" scope="context" source="logging.file" defaultValue="../logs"/>
<springProperty name="LOG_POINT_FILE" scope="context" source="logging.file" defaultValue="../logs/point"/>
<springProperty name="LOG_MAXFILESIZE" scope="context" source="logback.filesize" defaultValue="50MB"/>
<springProperty name="LOG_FILEMAXDAY" scope="context" source="logback.filemaxday" defaultValue="7"/>
<springProperty name="ServerIP" scope="context" source="spring.cloud.client.ip-address" defaultValue="0.0.0.0"/>
<springProperty name="ServerPort" scope="context" source="server.port" defaultValue="0000"/>
<!-- 彩色日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clrc" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="ewtpc"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="%clrc(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} [%clrc(%X{traceId}){yellow},%clrc(%X{X-B3-TraceId}){yellow}] %clrc(%level){blue} %clrc(%logger){cyan} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%ewtpc}"/>
<property name="CONSOLE_LOG_PATTERN_NO_COLOR"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId},%X{X-B3-TraceId}] %level %logger %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%ewtpc}"/>
<!-- 控制台日志 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<withJansi>true</withJansi>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 按照每天生成常规日志文件 -->
<appender name="DAILY_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}/${APP_NAME}/${APP_NAME}.log</file>
<!-- 基于时间的分包策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}/${APP_NAME}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<!--保留时间,单位:天-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN_NO_COLOR}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<logger name="com.ulisesbocchio" level="INFO"/>
<logger name="com.netflix" level="INFO"/>
<logger name="com.zaxxer.hikari" level="INFO"/>
<logger name="com.baomidou" level="INFO"/>
<logger name="org.example.spc" level="INFO"/>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DAILY_LOG"/>
</root>
</configuration>
main方法类App.java:
@EnableDiscoveryClient
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
}
ProductMapper.java
package org.example.spc.consumer.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.spc.consumer.model.Product;
import org.springframework.stereotype.Repository;
import java.util.List;
//@Mapper
@Repository
public interface ProductMapper {
Product findById(@Param("id") int id);
void insert(Product product);
List<Product> findAll();
}
ProductMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.spc.consumer.mapper.ProductMapper">
<select id="findById" parameterType="int" resultType="org.example.spc.consumer.model.Product">
SELECT id, name, price FROM product WHERE id = #{id}
</select>
<select id="findAll" resultType="org.example.spc.consumer.model.Product">
SELECT id, name, price FROM product
</select>
<insert id="insert" parameterType="org.example.spc.consumer.model.Product">
INSERT into product (id, name, price)
VALUES (#{id}, #{name}, #{price})
</insert>
</mapper>
ProductServiceImpl.java
@Service
public class ProductServiceImpl implements ProductService {
private static final Logger logger = LoggerFactory.getLogger(ProductServiceImpl.class);
@Autowired
private ProductMapper productMapper;
@Override
public Product getProduct(int id) {
logger.info("Find product by id : {}", id);
return productMapper.findById(id);
}
...
}
ProductController.java
@RestController
@RequestMapping("/product")
public class ProductController {
private static final Logger LOGGER = LoggerFactory.getLogger(ProductController.class);
@Autowired
private ProductService productService;
@GetMapping("/{id}")
@ResponseBody
public Product find(@PathVariable("id") int id) {
return productService.getProduct(id);
}
@GetMapping("/all")
@ResponseBody
public List<Product> findAll() {
return productService.findAll();
}
@PostMapping("/add")
public ResponseEntity addProduct(@RequestBody Product product) {
return productService.addProduct(product);
}
}
feignclientdemo重要源码和配置:
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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>feignclientdemo</artifactId>
<version>1.0-SNAPSHOT</version>
<name>feignclientdemo</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.7.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 18728
spring:
application:
name: feignclientdemo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: b2b3c55a-abe5-47a4-a47e-03c57138d1d7
FeignClientDemoApp.java (入口类)
package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableFeignClients
@EnableDiscoveryClient(autoRegister = false)
@SpringBootApplication
public class FeignClientDemoApp
{
public static void main( String[] args )
{
SpringApplication.run(FeignClientDemoApp.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
ProductService.java
package org.example.spc.consumer.service;
import org.example.spc.consumer.model.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@FeignClient("feigndemo") // 括号中的值必须和provider注册的应用名一致
public interface ProductService {
@GetMapping("/product/{id}")
Product getProduct(@PathVariable int id);
@GetMapping("/product/all")
List<Product> findAll();
@PostMapping("/product/add")
ResponseEntity addProduct(@RequestBody Product product);
}
ProductController.java
package org.example.spc.consumer.controller;
import org.example.spc.consumer.model.Product;
import org.example.spc.consumer.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 功能描述
*
* @auhtor sunlei
* @since 2024/7/28 17:38
*/
@RestController
@RequestMapping("/product")
public class ProductController {
private ProductService productService;
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping("/{id}")
@ResponseBody
public Product find(@PathVariable("id") int id) {
return productService.getProduct(id);
}
@GetMapping("/all")
@ResponseBody
public List<Product> findAll() {
return productService.findAll();
}
@PostMapping("/add")
public ResponseEntity addProduct(@RequestBody Product product) {
return productService.addProduct(product);
}
}
部署好后的调用结果如下:
GET请求:
POST请求:(用ApiFox调用)