Spring cloud gateway(Web MVC)的深入使用

 更新时间:2026年02月09日 09:59:13   作者:茗冬  
本文详细介绍了Spring Cloud Gateway的使用,包括工程搭建、路由配置、自定义过滤器和谓词的实现与使用,以及效果验证,感兴趣的朋友跟随小编一起看看吧

一定是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使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中Exception和Error的区别详解

    Java中Exception和Error的区别详解

    这篇文章主要介绍了Java中Exception和Error的区别详解,通过类的关系分析两者的区别与应用场景,包含代码实例,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • 2025.2.X 版本 IDEA maven 打包乱码问题的解决方案(最新整理)

    2025.2.X 版本 IDEA maven 打包乱码问题的解决方案(最新整理)

    IDEA 2025.2版本默认GBK编码导致Maven打包乱码,即使调整文件编码也无济于事,解决方法是修改Maven Runner设置,添加UTF-8环境变量,并清理缓存重启,建议在新建项目时同步设置,本文给大家介绍2025.2.X版本IDEA maven打包乱码问题的解决方案,感兴趣的朋友一起看看吧
    2025-10-10
  • Java实现链表的常见操作算法详解

    Java实现链表的常见操作算法详解

    这篇文章主要介绍了Java实现链表的常见操作算法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • 一文详解Java中的包装类和泛型

    一文详解Java中的包装类和泛型

    在Java中,由于基本类型不是继承⾃Object,为了在泛型中可以⽀持基本类型,Java给每个基本类型都对应了⼀个包装类型,有些情况下只有接收泛型才可以完成其功能,所以本文将给大家详细讲解一下Java中的包装类和泛型,需要的朋友可以参考下
    2025-04-04
  • springmvc Rest风格介绍及实现代码示例

    springmvc Rest风格介绍及实现代码示例

    这篇文章主要介绍了springmvc Rest风格介绍及实现代码示例,rest风格简洁,分享了HiddenHttpMethodFilter 的源码,通过Spring4.0实现rest风格源码及简单错误分析,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Spring Security实现禁止用户重复登陆的配置原理

    Spring Security实现禁止用户重复登陆的配置原理

    这篇文章主要介绍了Spring Security实现禁止用户重复登陆的配置原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Java Process中waitFor()的问题详解

    Java Process中waitFor()的问题详解

    这篇文章主要给大家介绍了关于Java Process中waitFor()问题的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-12-12
  • java实现文件变化监控的方法(推荐)

    java实现文件变化监控的方法(推荐)

    下面小编就为大家带来一篇java实现文件变化监控的方法(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决

    MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决

    这篇文章主要介绍了MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教。
    2022-01-01
  • Java 互相关联的实体无限递归问题的解决

    Java 互相关联的实体无限递归问题的解决

    这篇文章主要介绍了Java 互相关联的实体无限递归问题的解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10

最新评论