由防止表单重复提交引发的一系列问题--servletRequest的复制、body值的获取

HTTP请求与响应:Web开发核心机制详解 HTTP协议作为Web开发的基石,其请求(Request)与响应(Response)机制构成了客户端与服务器通信的基础框架。从技术原理看,HTTP协议采用无状态的请求-响应模型,通过纯文本格式传输数据,这带来了灵活性的同时也产生了编码问题等常见挑战。在工程实践中,GET与POST方法的正确选择、表单数据的编码处理、AJAX异步请求等技术细节直接影响系统性能和安全性。特别是在中文处理场景下,统一使用UTF-8编码能有效避免乱码问题。理解HTTP报文结构(包含请求行/状态行、头部字段和消息体)和状态码语义,是开 阅读详情

@Time:2019年1月4日 16:19:19

@Author:QGuo

 

背景:最开始打算写个防止表单重复提交的拦截器;网上见到一种不错的方式,比较合适前后端分离,校验在后台实现;

我在此基础上,将key,value。Objects.hashCode()了下

因为request的body 可能太大,过长;

但不保证存在不同的object生成的哈希值却相同,但是我们目的只是为了防止重复提交而已,不同对象生成哈希值相同的机率很小。

==========================代码==============================

1、HttpServletRequestReplacedFilter 过滤器.

目的:post请求时,复制request;注意代码中的注释部分;

package com.kdgz.service;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author QGuo
 * @date 2019/1/3 15:04
 */
public class HttpServletRequestReplacedFilter implements Filter {
    @Override
    public void destroy() {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            String contentType = request.getContentType();

            if (contentType != null && contentType.contains("application/x-www-form-urlencoded")) {
                //如果是application/x-www-form-urlencoded, 参数值在request body中以 a=1&b=2&c=3...形式存在,
                //若直接构造BodyReaderHttpServletRequestWrapper,在将流读取并存到copy字节数组里之后,
                //httpRequest.getParameterMap()将返回空值!
                //若运行一下 httpRequest.getParameterMap(), body中的流将为空! 所以两者是互斥的!
                request.getParameterMap();
            }
            if ("POST".equals(httpServletRequest.getMethod().toUpperCase())) {
                requestWrapper = new BodyHttpServletRequestWrapper((HttpServletRequest) request);
            }
        }

        if (requestWrapper == null) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter((HttpServletRequest)requestWrapper, response);
        }
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {}
}

2. HttpServletRequestWrapper --复制ServletRequest

目的在于:使servletRequest可以重复获取inputStream、reader;

package com.kdgz.service;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.Map;

/**
 * @author QGuo
 * @date 2019/1/3 15:05
 */
public class BodyHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    public byte[] getBody() { return body; }

    public void setBody(byte[] body) { this.body = body; }

    public BodyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = this.getBodyString(request).getBytes(Charset.forName("UTF-8"));
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream(),"UTF-8"));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(this.body);

        return new ServletInputStream() {
            @Override
            public boolean isFinished() {  return false; }

            @Override
            public boolean isReady() { return false; }

            @Override
            public void setReadListener(ReadListener readListener) {}

            @Override
            public int read() throws IOException { return bais.read(); }
        };
    }

    @Override
    public String getHeader(String name) { return super.getHeader(name); }

    @Override
    public Enumeration<String> getHeaderNames() { return super.getHeaderNames(); }

    @Override
    public Enumeration<String> getHeaders(String name) { return super.getHeaders(name); }

    @Override
    public Map<String, String[]> getParameterMap() { return super.getParameterMap(); }
    
    public String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}

3、web.xml 中添加过滤器

  <filter>
    <filter-name>httpServletRequestFilter</filter-name>
    <filter-class>com.kdgz.service.HttpServletRequestReplacedFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>httpServletRequestFilter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>

4、添加自定义注解

package com.kdgz.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author QGuo
 * @date 2018/12/24 13:58
 * 一个用户 相同url 同时提交 相同数据 验证
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SameUrlData {
}

5、添加拦截器

package com.kdgz.service;

import com.alibaba.fastjson.JSON;
import com.kdgz.annotation.SameUrlData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 一个用户 相同url 同时提交 相同数据 验证
 * 主要通过 session中保存到的url 和 请求参数。如果和上次相同,则是重复提交表单
 *
 * @author QGuo
 * @date 2018/12/24 14:02
 */
public class SameUrlDataInterceptor extends HandlerInterceptorAdapter {
    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            SameUrlData annotation = method.getAnnotation(SameUrlData.class);
            if (annotation != null) {
                if (repeatDataValidator(request)) {//如果重复相同数据
                    //在此可添加response响应内容,提醒用户重复提交了
                    return false;
                } else
                    return true;
            }
            return true;
        } else {
            return super.preHandle(request, response, handler);
        }
    }

    /**
     * 验证同一个url数据是否相同提交  ,相同返回true
     *
     * @param httpServletRequest
     * @return
     */
    public boolean repeatDataValidator(HttpServletRequest httpServletRequest) throws IOException {
        Map<String, String[]> parameterMap = new HashMap(httpServletRequest.getParameterMap());
        //删除参数中的v;(v参数为随机生成的字符串,目的是为了每次访问都是最新值,既然要防止重复提交,需要剔除此参数)
        if (parameterMap.containsKey("v"))
            parameterMap.remove("v");
        //每一位登录者都有唯一一个token认证
        String tokens = "";
        if (parameterMap.get("token").length > 0)
            tokens = parameterMap.get("token")[0];
        String method = httpServletRequest.getMethod().toUpperCase();//请求类型,GET、POST
        String params;
        if (StringUtils.equals(method, "POST")) {//post请求时
            BodyHttpServletRequestWrapper requestWrapper = new BodyHttpServletRequestWrapper((HttpServletRequest) httpServletRequest);
            byte[] bytes = requestWrapper.getBody();
            if (bytes.length != 0) {
                params = JSON.toJSONString(new String(bytes, "UTF-8").trim());
            } else {//若body被清空,则说明参数全部被填充到Parameter集合中了
                /**
                 * 当满足一下条件时,就会被填充到parameter集合中
                 * 1:是一个http/https请求
                 * 2:请求方法是post
                 * 3:请求类型(content-Type)是application/x-www-form-urlencoded
                 * 4: Servlet调用了getParameter系列方法
                 */
                Map<String, String[]> map = new HashMap(requestWrapper.getParameterMap());
                // 去除 v 参数
                if (map.containsKey("v"))
                    map.remove("v");
                params = JSON.toJSONString(map);
            }
        } else {
            params = JSON.toJSONString(parameterMap);
        }

        String url = String.valueOf(Objects.hashCode(httpServletRequest.getRequestURI() + tokens));
        Map<String, String> map = new HashMap<String, String>();
        map.put(url, params);
        //防止参数过多,string过大;现将储存为 hash编码;
        String nowUrlParams = String.valueOf(Objects.hashCode(map));
        String preUrlParams = stringRedisTemplate.opsForValue().get(url);
        if (preUrlParams == null) {//如果上一个数据为null,表示还没有访问页面
            //设置过期时间为3分钟
            stringRedisTemplate.opsForValue().set(url, nowUrlParams, 3, TimeUnit.MINUTES);
            return false;
        } else if (preUrlParams.equals(nowUrlParams)) {//否则,已经访问过页面
            //如果上次url+数据和本次url+数据相同,则表示重复添加数据
            return true;
        } else {//如果上次 url+数据 和本次url加数据不同,则不是重复提交,更新
            stringRedisTemplate.opsForValue().set(url, nowUrlParams, 3, TimeUnit.MINUTES);
            return false;
        }
    }
}

使用的时候,只要在接口上,添加注解即可

例如:

@RequestMapping(value = "v1.0/monGraphSave")
@SameUrlData
public AjaxMessage monGraphSave(@RequestBody MonGraphFB monGraphFB){} 

====================代码结束===================

 

整理至此,主要有以下注意点;

①、得考虑post请求参数获取的特殊性

②、request.getInputStream() 只能获取一次,要想可以多次读取,得继承HttpServletRequestWrapper,读出来--放回去

③、过滤器的目的是可以直接读取request里面的body

④、request参数body可能很大,可以取hash值。

⑤、key、value的存储,需要设置过期时间;

 

心得:

其实我觉得防止表单重复提交这个功能,作用不是特别大;因为只要随便加一个参数,就可以把需要的参数重复添加进系统中;

只能做到,防止用户误操作,点击了多次这种情况;(一般前端也会做处理的,但万一前端抽风自动发起了多次请求呢);

只能说一定程度上 更加完善吧

 

改进:

可以在SameUrlDataInterceptor拦截器中,添加response响应内容,让用户知道自己重复提交了。

很简单不举例;

SpringBoot集成Hutool构建XSS过滤器:从原理到实战的Web安全防护指南 在Web应用开发中,数据安全是保障系统稳定运行的核心要素。跨站脚本攻击(XSS)作为一种常见的安全威胁,其原理在于攻击者向网页中注入恶意脚本,当用户浏览时执行,从而窃取信息或进行会话劫持。为了有效防御此类攻击,后端输入过滤成为不可或缺的技术手段,它能在数据进入业务逻辑前进行净化,从源头遏制风险。基于SpringBoot框架,结合国产高效的Hutool工具库,可以快速构建一个高性能、可配置的XSS过滤器。该方案通过包装HttpServletRequest,对请求参数、请求头及JSON请求体进行精准过滤,并支持 阅读详情

相关推荐

从.har文件到Postman集合:一键转换的完整教程(附避坑指南)

本文详细介绍了如何将.har文件高效转换为Postman集合的完整教程,涵盖基础操作到高级技巧,包括认证信息处理、环境变量提取等痛点问题的解决方案。通过抓包工具记录的.har文件,开发者可以快速生成Postman集合,提升API调试与测试效率,特别适合前后端分离开发模式下的团队协作。

weixin_26795617的博客 384

java HttpServletRequest拷贝解决HttpServletRequest 中流只能读取一次的问题

目录一、遇到的问题:二、原因:三、解决办法:四、测试: 一、遇到的问题: 接口上传了一个文件,从HttpServletRequest 中获取了文件名、文件内容等信息,后面再从HttpServletRequest 中读取文件名信息、文件内容就读取不到了 二、原因: 因为流是单向的,就像水一样被舀了一瓢就会少一瓢。所以 HttpServletRequest 对象中的 InputStream 或者 Reader 只能使用一次。 三、解决办法: 那该怎么办,复制一份HttpServletRequest 对象,把流转

西凉的悲伤博客 4731

c++多线程(1)------创建和管理线程td::thread

std::thread 是 C++11 标准库中用于创建和管理线程的核心类,定义在 <thread> 头文件中。它使得多线程编程变得简单、类型安全且跨平台。

jndingxin的专栏 647

8.8、HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest对象中,通过HttpServletRequest的方法,可以获取客户端的所有信息 公共接口类HttpServletRequest继承自ServletRequest。客户端浏览器发出的请求被封装成为一个HttpServletRequest对象。对象包含了客户端请求信息包括请求的地址,请求的参数,提交的数据,上传的文件客户端的ip甚至客户端操作系统都包..

qq_44840451的博客 587

NotImplementedError: Cannot copy out of meta tensor; no data! Please use torch.nn.Module.to_empty()

Please use torch.nn.Module.to_empty() instead of torch.nn.Module.to()输出(llama) (base) [root@localhost home]# nvidia-smi Unable to determine the device handle for GPU0000:09:00.0: Unknown Error。这是典型的“孤儿进程”,但它还是。(僵尸进程),占用 GPU 资源。如果进程仍然不消失,并且。,这意味着它的父进程是。

m0_61636632的博客 2096

20 Embedding 与向量检索:语义为什么能变成距离

Embedding 是 RAG 语义检索的数学基础。本文用工程视角讲清文本如何变成向量,为什么语义相近会表现为距离相近,Top-K 检索如何工作,以及在模型选择、同模型约束、精确匹配和召回评估上的常见坑。

老于的笔记的专栏 6202

Java 复制HttpServletRequest InputStream的方法 只需2步轻松搞定

你可能很疑惑,为什么request对象中的InputStream或者Reader只能使用一次? 原理很简单,可以把流比喻成水,request里面的inputStream就好比杯子中的水。试问杯子中的水倒掉之后还能继续倒吗?当然不能滴!InputStream里面有做指针和同步处理,一旦指针到了末尾是不会回来的。那么我们怎么拷贝request body里面的数据呢,当然我们得找一种可以复制的存储方式...

一只猿的自我修养 9550

YOLOv8【第七章:损失函数篇·第1节】一文搞定,IoU系列损失函数!

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。

**My Coding Family** 1195

SpringBoot笔记:统一请求参数修改(HttpServletRequest流复制),加解密参数也可参考处理

需要进行统一的解密请求 header 头里面的关键字 encryKey ,将解密出来的值赋给 provinceId 并传递给后端的每一个请求接口,并通过 provinceId 字段进行`数据分权`。

u011047968的专栏 4074

【HOJ系列】HOJ判题机部署

【HOJ系列】HOJ判题机部署

随风浪仔的专栏 903

Python爬招聘网站:Selenium+ActionChains破解滑动验证码,批量采集岗位数据(新手可跑)

通过本文实战,你不仅学会了用Selenium+ActionChains破解滑动验证码,还掌握了爬虫的核心优化思路——“模拟人类行为”是突破反爬的关键。这套逻辑不仅适用于招聘网站,还能迁移到其他有滑动验证码的平台(如电商、资讯网站)。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1185

java 复制request流_java HttpServletRequest 重复流读取

在用reset接口的时候,常常会使用request.getInputStream()方法,但是流只能读取一次,一旦想要加上一个过滤器用来检测用户请求的数据时就会出现异常。在过滤器中通过流读取出用户post提交过来的数据,这是流已经读取了一次,那么该流就已经作废了,所以在contorller再次读取用户请求的数据时就会抛出异常。解决方法方法一:参见:http://www.cnblogs.com/ji...

weixin_30823291的博客 1559

inno setup自定义卸载程序和美化

也就是DisplayName,因为我们在第一步的时候,将卸载程序名称也写入了注册表,所以,我们直接从注册表读DisplayName字段即可。1.要实现inno setup自定义卸载程序,首先需要关闭自带的卸载程序。在[Setup]的字段关闭卸载程序步骤和页面。2.删除应用程序目录里能够删除的并且返回进度。3.自定义卸载程序相关的注册表信息。4.删除启动菜单栏和guid注册表。2.自定义快捷方式和程序菜单。

cs821984831的博客 2347

Servlet请求参数获取全解析:从getParameter到乱码排查

在Java Web开发中,Servlet作为服务端处理HTTP请求的核心组件,其参数获取机制是每个开发者都必须掌握的基础技能。HttpServletRequest接口提供了getParameter、getParameterValues、getParameterMap等API,它们的设计根植于HTTP表单的数据模型,用以支持单值、多值及整体遍历等不同场景。理解这些API背后的解析流程,尤其是GET与POST请求在参数传递位置和编码链路上的本质区别,是定位中文乱码、参数丢失等问题的关键。从字符编码的原理出发,统

weixin_34320159的博客 301

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

面试问题总结

文章目录1.abstract和interface异同2.构造方法3.final关键字作用4.重写和重载的区别4.1重载4.2重写5.this关键字6.封装的作用,实现封装的步骤7.封装中的getter和setter方法分别作用7.1setter方法7.2getter方法8.Mybatis-plus与Spring Data JPA9.装箱和拆箱9.1装箱9.2拆箱10.ArrayList底层原理11.反射12.Filter13.$与#的区别14.js中的DOM对象与jQuery中的DOM区别15.四大元注解1

肥猫不是猫 1109

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

springboot前后端分离Java后端跨越问题解决

本文介绍了基于JWT的前后端分离认证方案,通过SpringBoot实现完整的登录、Token生成与验证流程。主要内容包括:1. JWT原理与前后端分离架构优势;2. SpringBoot项目搭建与JWT依赖配置;3. 登录接口实现与Token生成;4. 双Token机制(AccessToken+RefreshToken)的实现;5. JWT过滤器配置与受保护接口开发;6. 跨域处理方案;7. 详细测试验证过程;8. JWT安全风险分析与防范措施。文章提供了完整代码示例,涵盖Token刷新、黑名单、密钥管理等

ywl470812087的博客 10万+

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Java:110-SpringMVC的底层原理(上篇)

大致流程如下: 对应的web.xml: 在pom.xml中加上如下依赖: 然后再web.xml中加上如下: 一般tomcat的web.xml在其conf目录里面,如图: 在java资源文件夹下,创建com.controller包,然后创建DisController类: 在WEB-INF文件夹下,创建jsp目录,然后创建如下success.jsp文件: 在资源文件夹下加上springmvc.xml文件: 补充web.xml: 假设项目是springmvc这个名称,端口是8080,那么启动服务

qq_59609098的博客 1451

Java Servlet Filter 原理与生产级实战:从请求拦截到链式控制

Servlet Filter 是 Java Web 请求处理流水线的核心组件,其本质是基于责任链模式的容器级拦截机制,运行在 DispatcherServlet 之前,可捕获所有 HTTP 请求(含静态资源、转发、错误页等)。它通过 init/doFilter/destroy 三阶段管理生命周期,依赖 dispatcherTypes 控制触发时机,依靠 filter chain 顺序决定执行逻辑。技术价值在于统一处理跨域、字符编码、日志、权限等横切关注点,避免业务代码污染。典型应用场景包括中文乱码治理、To

weixin_34290631的博客 342

Spring Boot接口日志:基于AOP与自定义注解的可观测性实践

接口请求日志是微服务可观测性的基础能力,其本质是通过结构化方式捕获HTTP请求的输入、处理与输出全过程。传统Filter方案受限于流不可重复读取,难以获取Controller层原始参数与返回对象;而Spring AOP结合自定义注解(如@Loggable)可在方法执行前后精准切面,实现请求体、响应体、异常堆栈、耗时等关键字段的可控采集。该方案支持按需启用、分级日志(BASIC/DETAIL/DEBUG)、字段级脱敏与traceId链路透传,兼顾安全性、可维护性与低侵入性,适用于Spring Boot 2.x

weixin_34203832的博客 459

Spring Boot 3.x 集成 QQ 邮箱验证码:5步配置与 2分钟过期策略实现

本文详细介绍了在Spring Boot 3.x项目中集成QQ邮箱发送验证码的完整实现方案,包括获取SMTP授权码、配置邮件服务、实现验证码生成与发送功能,并重点讲解了2分钟过期策略的实现方法。通过清晰的代码示例和配置说明,帮助开发者快速实现安全可靠的邮箱验证码功能。

weixin_33747129的博客 425

详解分布式事务 - Seata

Seata 是阿里开源的**分布式事务解决方案**,用于解决微服务跨服务、跨数据库的数据一致性问题,实现业务上的事务回滚与提交。 核心角色: - **TC 事务协调器**:全局事务管理器,维护全局事务状态,通知各分支提交 / 回滚。 - **TM 事务管理器**:业务侧开启、提交、回滚全局事务。 - **RM 资源管理器**:管理本地分支事务,向 TC 注册分支,执行分支提交回滚。

后端胖头鱼的博客 712

JavaWeb学习

#前端部分(了解)课程思路、前端要素(Web了解、html、JS、vue、ajax、Element等)传统路线现在课程大纲路线## 前端要素(Web了解、html)HTML语法特点基本标签html标签(span)html标签(a)html标签(video)盒子模型:html标签(div)html标签(表格)html标签(表单)## 前端要素(JS)JS引入方式JS基础语法JS函数JS对象(基础(只关注Array、String、JSON)、BOM、DOM)JS事件监听## 前端要素(Vue)插值表达式Vue常

qq_2634545805的博客 430

Log4j2全局logId配置指南:从MDC到异步与微服务透传

在分布式系统和复杂应用中,日志是诊断问题的关键。传统依赖时间戳和线程名的日志记录方式,在异步、高并发场景下难以串联请求的完整轨迹,导致问题排查效率低下。其核心原理是为每个请求分配一个全局唯一的标识符(如logId或traceId),通过线程上下文(如MDC)进行传递,使同一次请求在所有相关日志行中携带相同ID,从而实现端到端的追踪。这项技术的核心价值在于将杂乱的日志信息转化为可串联、可分析的诊断数据,极大提升了线上问题的定位速度和系统可观测性。典型的应用场景包括Web请求追踪、微服务调用链路还原以及异步任务

weixin_30268071的博客 365
上一篇: 由防止表单重复提交引发的一系列问题--servletRequest的复制、body值的获取
下一篇: 单例模式(java)
JavaOpsPro
博客等级 码龄11年 1892粉丝 42原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JavaOpsPro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值