Spring cloud gateway(Web MVC)的深入使用
一定是spring boot项目,spring cloud gateway基于spring boot
前言
提示:本文所用的是spring cloud gateway web mvc特性
本文提供了对spring cloud gateway网关的使用案例,并没有列举出所有过滤器或谓词的使用示例,但是包含了从工程搭建及基本使用至概念理解再到一些自定义客制化的使用。为你打开基本使用到深入使用的一扇门。所配关键代码都有注释,一定可以为你答疑解惑。
一、前置条件
提示:spring cloud gateway是基于spring boot,项目一定是spring boot项目,一定是spring boot项目
jdk:17
构建工具:maven 3.6.3
spring相关jar:spring-cloud-starter-gateway-server-webmvc 4.3.0(必须),spring boot 3.5.8(被间接依赖至项目中),spring cloud 2025.0.0(作为pom)
参考资料:https://docs.spring.io/spring-cloud-gateway/reference/4.3/spring-cloud-gateway-server-webmvc.html
开发工具:idea 2021.2
二、工程搭建(创建一个普通maven工程,添加必要依赖及构建工具即可,本案例是一个maven子工程)
提示:按需可引入spring boot starter parent,直接按需用spring 配套的插件及依赖包,更为方便
父工程pom.xml (仅列出关键依赖)

工程目录

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>springCloudMicroService</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gatewayServerWebMvcDemo</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<!-- 设置编码格式,防止maven打包构建时没有统一编码格式导致有中文注释的文件解析异常,影响项目启动 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- spring boot构建相关配置 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.5.8</version>
<configuration>
<mainClass>${start-class}</mainClass>
<layout>JAR</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<!-- 【很关键】自定义${}占位符,用于将pom文件的变量动态替换到applicaton**.yml的某处
例如这里的application.yml文件,使用${spring.profiles.active}取
将pom.xml->properties->profile->properties->spring.profiles.active属性值
-->
<delimiters>
<delimiter>${}</delimiter>
</delimiters>
<!-- 禁用默认的占位符@xxPropertiesName@ -->
<useDefaultDelimiters>false</useDefaultDelimiters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- 根据需要开启或关闭 -->
<includes>
<include>public/**</include>
<include>static/**</include>
<include>templates/**</include>
<include>mapper/**</include>
<include>properties/**</include>
<include>i18n/**</include>
<include>logback*.xml</include>
<!-- 包含默认的配置文件,这个配置文件仅有spring.profiles.active属性,这是不加profiles启动的关键 -->
<include>application.yml</include>
<!-- 按maven不同的打包参数,打包不同的环境相关的配置文件 -->
<include>application-${spring.profiles.active}.*</include>
<include>log*-${spring.profiles.active}.xml</include>
</includes>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
<!-- profiles 相关配置 -->
<profiles>
<!--
profile中的<id>div</id>元素值指的是-P 的参数, 如:mvn clean install -P div
profile中的<properties><spring.profiles.active>dev</spring.profiles.active></properties>中的spring.profiles.active元素及值
值指的是-D 的参数, 如:mvn clean install -Dspring.profiles.active=dev
-->
<profile>
<!-- 开发环境 -->
<id>dev</id>
<properties>
<spring.profiles.active>dev</spring.profiles.active>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!-- 测试环境 -->
<profile>
<id>sit</id>
<properties>
<spring.profiles.active>sit</spring.profiles.active>
</properties>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
</profile>
<!-- 生产环境 -->
<profile>
<id>prod</id>
<properties>
<spring.profiles.active>prod</spring.profiles.active>
</properties>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
</profile>
</profiles>
</project>三、功能实现范围
- 配置文件或java编程方式的路由配置
- 自定义过滤器(filter)、自定义谓词(predicate)的实现与使用
- 路由到本服务接口(就是用于spring mvc的函数式端点的使用)
四、需要路由访问的服务(应用)准备
网关自身(服务/应用):http://localhost:8888
应用一:http://localhost:8081/entitleservice
应用二:http://localhost:8082/functonservletpath
五、路由配置(配置文件方式)
提示:两种路由配置方式二选一,二选一,二选一!!!
默认配置文件(application.yml)
server:
server-name: gatewaymvcdemo
port: 8888
spring:
profiles:
# active: javaconfig
active: fileconfig路由配置文件(application-fileconfig.yml)
# 使用配置文件方式配置路由
spring:
cloud:
gateway:
server:
webmvc:
routes:
# 配置路由到function demo服务
- id: function_demo_route
uri: http://localhost:8082
predicates:
- Method=GET,PUT,DELETE,POST
- Path=/functionservletpath/**
filters:
- AddRequestParameter=code, gateway for functiondemo
- AddRequestHeader=function-Request-Id,function_header_value
- id: entitle_route
uri: http://localhost:8081
predicates:
- Method=GET,PUT,DELETE,POST
- Path=/entitleservice/info/**,/entitleservice/gatewayInfo/**,/entitleservice/user/**,/entitleservice/role/**
- predicateHeaderExists=custom_header
filters:
- AddRequestParameter=code, gate way for entitleservice
- AddRequestHeader=entitle-Request-Id,entitle_header_value
- instrumentForFilter=req_header,rep_header六、路由配置(java编程方式)
提示:两种路由配置方式二选一,二选一,二选一!!!!
默认配置文件(application.yml)
server:
server-name: gatewaymvcdemo
port: 8888
spring:
profiles:
active: javaconfig路由配置文件(application-javaconfig.yml)提示:路由配置文件为空,因使用java编程方式配置
java配置文件(ApplicationRoutingConfig.java)
package com.mrhan.config;
import com.mrhan.service.GatewayHandler;
import com.mrhan.service.UserHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.function.*;
import java.time.ZonedDateTime;
import java.util.function.BiFunction;
import java.util.function.Function;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.*;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.*;
import static org.springframework.web.servlet.function.RequestPredicates.accept;
/**
* @author Mr han
* @date 2025/12/6
* @description 路由配置,此处配置了路由后,application.yml中配置的路由将无效,两种配置只能存在一种,不能并存
* 同等效果配置文件方式配置@see application.yml,自定义的filter和predicate在两种配置方式都能够生效
*
*
*/
@Configuration(proxyBeanMethods = false)
@Profile("javaconfig")
public class ApplicationRoutingConfig {
private static final Logger log = LoggerFactory.getLogger(ApplicationRoutingConfig.class);
/**
*
* @return
* @date 2025/12/17
* @description 使用java代码方式配置路由,等价于@see application.yml文件配置方式
*
*/
@Bean
public RouterFunction<ServerResponse> customRoutes(GatewayHandler gatewayHandler) {
// @formatter:off
return route("path_route")
.GET("/gateway/getRole", accept(MediaType.TEXT_HTML),gatewayHandler::getRole)
.POST("/gateway/updateRole/{roleId}", accept(MediaType.APPLICATION_JSON), gatewayHandler::updateRole)
.before(request -> {
String path = request.requestPath().value();
log.error("=========开始进入本服务请求:{}=========",path);
return request;
})
// 添加请求之后的函数处理器
.after((request,response) -> {
log.error("==========本服务请求处理完毕:{},响应的状态码:",request.requestPath().value(),response.statusCode());
return response;
})
.build().and(route("entitle_route")
.route(path("/entitleservice/**").and(SampleRequestPredicates.predicateHeaderExists("entitle_header")), http())
.before(uri("http://localhost:8081"))
.before(addRequestParameter("code","Java gateway for entitlementservice"))
.before(addRequestHeader("java_config_header","java config value"))
.filter(SampleHandlerFilterFunctions.instrumentForFilter("req_header","rep_header"))
.build().and(route("function_demo_route")
.route(path("/functionservletpath/**").and(SampleRequestPredicates.predicateHeaderExists("function_header")), http())
.before(uri("http://localhost:8082"))
.before(addRequestParameter("code","Java config gateway for functiondemo"))
.before(addRequestHeader("function-Request-Id","function_header_value"))
.build()));
}
/**
* @author Mr han
* @date 2025/12/15
* @description 自定义的predicate 是否包含某个固定的请求头,此类的实现等等同于方法 @see {@link SampleRequestPredicates#predicateHeaderExists(String)}
*
*/
private static class CustomHeaderExists implements RequestPredicate {
// 请求头名称
private static final String HEADER_NAME = "custom_header";
/**
*
* @return
* @date 2025/12/15
* @description 需要包含custom_header请求头
*
*/
@Override
public boolean test(ServerRequest request) {
return request.headers().asHttpHeaders().containsKey(HEADER_NAME);
}
}
/**
*
* @return
* @date 2025/12/15
* @description 使用函数式的方法,自定义过滤器,使用函数式方法实现过滤器
* 在自定义的过滤器中可以实现日志的打印,已经权限的认证等逻辑,这里仅仅是增加请求头及响应头
* 此方法等同于@see {@link SampleHandlerFilterFunctions#instrumentForFilter(String, String)}
*
*/
private static HandlerFilterFunction<ServerResponse, ServerResponse> instrumentWithFilter(String requestHeader,String responseHeader){
return (request,next) -> {
ServerRequest modifier = ServerRequest.from(request).header(requestHeader, "hello").build();
ServerResponse responseHandler = next.handle(modifier);
responseHandler.headers().add(responseHeader, "world");
return responseHandler;
};
}
/**
*
* @return
* @date 2025/12/15
* @description 使用函数式的方法,定义before方式的过滤器
*
*/
private static Function<ServerRequest, ServerRequest> instrumentWithBefore(String requestHeader){
return request -> {
return ServerRequest.from(request).header(requestHeader, "before_header_value").build();
};
}
/**
*
* @return
* @date 2025/12/15
* @description 使用函数式的方法,定义after方式的过滤器
*
*/
private static BiFunction<ServerRequest, ServerResponse, ServerResponse> instrumentWithAfter(String header){
return (request,response) -> {
response.headers().add(header, "after_header_value");
return response;
};
}
}七、自定义过滤器
· 提示:定义的过滤器类按一个类一个静态方法,多增加几个静态方法spring也只会加载第一个静态方法,静态方法一定要加@Shotcut注解,不然启动报错,报错原因分析及解决,将在另一篇关于gateway疑难杂症解决的文章中展开。
过滤器定义(SampleHandlerFilterFunctions.java):一个类一个静态过滤器方法(方法上一定要加上@Shortcut注解),多加了也没用spring只会加载第一个静态方法
package com.mrhan.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.server.mvc.common.Configurable;
import org.springframework.cloud.gateway.server.mvc.common.Shortcut;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.function.HandlerFilterFunction;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
import java.util.Objects;
import java.util.function.Function;
/**
* @author Mr han
* @date 2025/12/15
* @description 定义包含所有自定义filter的函数式过滤器类,供filtersupplier类使用暴露到项目中
* 一个类只能定义一个静态方法,spring cloud gateway只注册一个静态方法
*/
public class SampleHandlerFilterFunctions {
private static final Logger log = LoggerFactory.getLogger(SampleHandlerFilterFunctions.class);
@Shortcut
public static HandlerFilterFunction<ServerResponse, ServerResponse> instrumentForFilter(String reqHeaderName,String repHeaderName) {
return (request, next) -> {
log.error("==========进入自定义filter处理逻辑,请求头:{},响应头{}===========",reqHeaderName,repHeaderName);
ServerRequest modified = ServerRequest.from(request)
.header(reqHeaderName, "request header for filter").build();
ServerResponse response = next.handle(modified);
response.headers().add(repHeaderName,"response header for filter" );
return response;
};
}过滤器提供者(CustomFilterSupplier.java):就是一个继承了SimpleFilterSupplier类的类而已,重写了继承方法
package com.mrhan.config;
import org.springframework.cloud.gateway.server.mvc.filter.SimpleFilterSupplier;
/**
* @author Mr han
* @date 2025/12/15
* @description 创建字对应的filterSupplier,用于注册提供自定义的filter
*/
public class CustomFilterSupplier extends SimpleFilterSupplier {
public CustomFilterSupplier() {
super(SampleHandlerFilterFunctions.class);
}
}过滤器配置类(FilterConfiguration.java):只是为了将过滤器提供者注册为Bean
package com.mrhan.config;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
/**
* @author Mr han
* @date 2025/12/17
* @description 注册自定义的过滤器,用于配置在application.yml文件中,目前使用此配置方式无实际效果 TODO
*/
@Configuration
@Profile("fileconfig")
public class FilterConfiguration {
private static final Logger log = LoggerFactory.getLogger(FilterConfiguration.class);
@PostConstruct
public void init(){
log.error("========开始加载FilterConfiguration配置============");
}
@Bean
public CustomFilterSupplier customFilterSupplier() {
return new CustomFilterSupplier();
}
}八、自定义谓词
提示:定义的谓词按一个类一个静态方法,多增加几个静态方法spring也只会加载第一个静态方法,静态方法一定要加@Shortcut注解,不然启动报错,报错原因分析及解决,将在另一篇关于gateway疑难杂症解决的文章中展开。
谓词定义(SampleRequestPredicates.java):一个类一个静态过滤器方法(一定要加上@ShortCut注解),多加了也没用spring只会加载第一个静态方法
package com.mrhan.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.server.mvc.common.Configurable;
import org.springframework.cloud.gateway.server.mvc.common.Shortcut;
import org.springframework.web.servlet.function.RequestPredicate;
/**
* @author Mr han
* @date 2025/12/15
* @description 定义包含自定义Predicate的所有实现的类
*/
public class SampleRequestPredicates {
private static final Logger log = LoggerFactory.getLogger(SampleRequestPredicates.class);
/**
*
* @return
* @date 2025/12/15
* @description 请求头的判断(是否包含某个名称的请求头),必须添加@Configurable注解且只能有一个参数,不然无法生效
*
*/
@Shortcut
public static RequestPredicate predicateHeaderExists(String name) {
log.error("======进入了自定义predicate的处理逻辑所需请求头:{}==========",name);
return request -> {
return request.headers().asHttpHeaders().containsKey(name);
};
}
}谓词提供者(CustomPredicateSupplier.java):就是一个实现了PredicateSupplier接口的类
package com.mrhan.config;
import org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
public class CustomPredicateSupplier implements PredicateSupplier {
@Override
public Collection<Method> get() {
return Arrays.asList(SampleRequestPredicates.class.getMethods());
}
}谓词配置类(PredicateConfiguration.java):只是为了将谓词提供者注册为Bean
package com.mrhan.config;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
/**
* @author Mr han
* @date 2025/12/17
* @description 注册自定义的predicate,用在application.yml中
*/
@Configuration
@Profile("fileconfig")
public class PredicateConfiguration {
private static final Logger log = LoggerFactory.getLogger(PredicateConfiguration.class);
@PostConstruct
public void init(){
log.error("========开始加载PredicateConfiguration配置============");
}
@Bean
public CustomPredicateSupplier customPredicateSupplier() {
return new CustomPredicateSupplier();
}
}九、自定义谓词、自定义过滤器的使用(配置文件或java编程方式)
配置文件方式:格式"静态方法名(首字母大写小写都可)=参数值"

java编程方式:直接调用过滤器或谓词方法即可

十、效果验证(配置文件方式的路由效果)
未配置指定请求头:访问404

配置指定请求头:验证自定义的过滤器,谓词等效果

十一、一些疑问、困惑的解释
- 配置中的uri是干嘛的?
解释:处理转发过来的请求的服务器地址 - 怎么理解网关?
解释:将匹配(含你定义的请求路径、携带了某个请求头、请求参数等)你谓词(predicate)的请求经过一系列处理(过滤器的路径重写、添加请求头、参数等)后转给你指定的处理器(可能是访问的服务地址,可能是你指定处理该请求的一个方法)做处理,是请求访问的守门员。
总结
到此这篇关于Spring cloud gateway(Web MVC)的使用全集的文章就介绍到这了,更多相关Spring cloud gateway使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
2025.2.X 版本 IDEA maven 打包乱码问题的解决方案(最新整理)
IDEA 2025.2版本默认GBK编码导致Maven打包乱码,即使调整文件编码也无济于事,解决方法是修改Maven Runner设置,添加UTF-8环境变量,并清理缓存重启,建议在新建项目时同步设置,本文给大家介绍2025.2.X版本IDEA maven打包乱码问题的解决方案,感兴趣的朋友一起看看吧2025-10-10
Spring Security实现禁止用户重复登陆的配置原理
这篇文章主要介绍了Spring Security实现禁止用户重复登陆的配置原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下2019-12-12
MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决
这篇文章主要介绍了MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教。2022-01-01


最新评论