顺丰同城急送API对接(附源码)

一、背景

最近公司让我对接顺丰同城急送的API,讲讲里面需要注意的几点

官方的API文档有些示例代码也不全,具体细节不多说,如果你现在也需要对接他们API,可以参考本篇博客再配合官方文档结合起来看,可以让您再开发的时候少掉两根头发,对您会有一定帮助的

官网api文档

首先你们要对接他们产品之前,需要得到账号,账号这边是同事给我的,

开始对接之前,必须要搞清楚你们对接的是店铺还是企业版的,区别就是企业版本的顺丰官方会给你一个卡号,是月结卡,你公司本月下的单,扣费就在这个卡里面扣款,然后月底结算费用,店铺的是没有的(其他的后续我再补充)

二、代码

用他这个签名是没毛病的,但是如果你传入的数据只有一层对象,那么是可以的,但是,如果你传入的数据是二维map甚至更多层级(这里根据他的接口参数决定),那这样再调用他的sign签名会出问题,因为他这个postData参数必须是json格式的字符串,而且还要排序的,postData里面是你这个接口所有的参数,具体代码还是看我的这个更实在,sign具体看generateOpenSign方法:

package com.admin.business.controller.sfsamecity;

import com.admin.util.HttpUtils;
import com.alibaba.fastjson.JSONObject;
import net.sf.json.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import java.io.IOException;
import java.security.MessageDigest;
import java.util.*;

import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Component;

@Component
public class SFHapper {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());


    @Value("${sf.same.city.devId}")
    private String devId;
    @Value("${sf.same.city.secretKey}")
    private String secretKey;


    /**
     * 预创建订单(店铺)
     * https://openic.sf-express.com/open/api/docs/index#/apidoc
     */
    public String precreateorder(Map<String, Object> param){
        try {
            param.put("dev_id",Integer.parseInt(devId));
            String jsonString = formatAndSortMap(param,0);
            String sign = generateOpenSign(jsonString,Integer.parseInt(devId),secretKey);
            String resultStr = null;
            try {
                resultStr = HttpUtils.postJson("https://openic.sf-express.com/open/api/external/precreateorder?sign="+sign, jsonString);
            }catch (Exception e){
                e.printStackTrace();
            }
            return resultStr;

        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return null;
    }

    /**
     * https://openic.sf-express.com/open/api/docs/index#/apidoc
     * 创建订单(店铺)
     *
     */
    public String createorder(Map<String, Object> param){
        try {
            param.put("dev_id",Integer.parseInt(devId));
            String jsonString = formatAndSortMap(param,0);
            String sign = generateOpenSign(jsonString,Integer.parseInt(devId),secretKey);
            String resultStr = null;
            try {
                resultStr = HttpUtils.postJson("https://openic.sf-express.com/open/api/external/createorder?sign="+sign, jsonString);
            }catch (Exception e){
                e.printStackTrace();
            }
            return resultStr;

        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return null;
    }



    /**
     * 预创建订单(企业)
     * https://openic.sf-express.com/open/api/docs/index#/apidoc
     */
    public String precreateorderEnterprise(Map<String, Object> param){
        try {
            param.put("dev_id",Integer.parseInt(devId));
            String jsonString = formatAndSortMap(param,0);
            String sign = generateOpenSign(jsonString,Integer.parseInt(devId),secretKey);
            String resultStr = null;
            try {
                resultStr = HttpUtils.postJson("https://openic.sf-express.com/open/api/external/precreateorder4c?sign="+sign, jsonString);
            }catch (Exception e){
                e.printStackTrace();
            }
            return resultStr;

        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return null;
    }

    /**
     * https://openic.sf-express.com/open/api/docs/index#/apidoc
     * 创建订单(企业)
     *
     */
    public String createorderEnterprise(Map<String, Object> param){
        try {
            param.put("dev_id",Integer.parseInt(devId));
            String jsonString = formatAndSortMap(param,0);
            String sign = generateOpenSign(jsonString,Integer.parseInt(devId),secretKey);
            String resultStr = null;
            try {
                resultStr = HttpUtils.postJson("https://openic.sf-express.com/open/api/external/createorder4c?sign="+sign, jsonString);
            }catch (Exception e){
                e.printStackTrace();
            }
            return resultStr;

        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return null;
    }

    /**
     * https://openic.sf-express.com/open/api/docs/index#/apidoc
     * 预取消订单
     */
    public String precancelorder(Map<String, Object> param){
        try {
            param.put("dev_id",Integer.parseInt(devId));
            String jsonString = formatAndSortMap(param,0);
            String sign = generateOpenSign(jsonString,Integer.parseInt(devId),secretKey);
            String resultStr = null;
            try {
                resultStr = HttpUtils.postJson("https://openic.sf-express.com/open/api/external/precancelorder?sign="+sign, jsonString);
            }catch (Exception e){
                e.printStackTrace();
            }
            return resultStr;

        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return null;
    }


    /**
     * https://openic.sf-express.com/open/api/docs/index#/apidoc
     * 取消订单(店铺)
     */
    public String cancelorder(Map<String, Object> param){
        try {
            param.put("dev_id",Integer.parseInt(devId));
            String jsonString = formatAndSortMap(param,0);
            String sign = generateOpenSign(jsonString,Integer.parseInt(devId),secretKey);
            String resultStr = null;
            try {
                resultStr = HttpUtils.postJson("https://openic.sf-express.com/open/api/external/cancelorder?sign="+sign, jsonString);
            }catch (Exception e){
                e.printStackTrace();
            }
            return resultStr;

        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return null;
    }

    /**
     * https://openic.sf-express.com/open/api/docs/index#/apidoc
     * 获取配送员轨迹H5(店铺)
     */
    public String riderviewv2(Map<String, Object> param){
        try {
            param.put("dev_id",Integer.parseInt(devId));
            String jsonString = formatAndSortMap(param,0);
            String sign = generateOpenSign(jsonString,Integer.parseInt(devId),secretKey);
            String resultStr = null;
            try {
                resultStr = HttpUtils.postJson("https://openic.sf-express.com/open/api/external/riderviewv2?sign="+sign, jsonString);
            }catch (Exception e){
                e.printStackTrace();
            }
            return resultStr;

        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return null;
    }


    public static void main(String[] args) throws IOException {

        Map<String, Object> param = new HashMap<>();

        param.put("shop_id","3243xxx93");
        param.put("dev_id",1691xxx52);
//        param.put("shop_type",1);
        param.put("user_lng","1xx.16427833749388");
        param.put("user_lat","2xx.558482814127863");
        param.put("user_address","广东省深圳市罗湖区中xxxxxx");
        param.put("weight","20");
        param.put("product_type",18);

        // 转换为秒级时间戳
        long timestampInMillis = System.currentTimeMillis();
        long timestampInSeconds = timestampInMillis / 1000;
        param.put("push_time",timestampInSeconds);
        System.out.println(timestampInSeconds);
//        param.put("total_price","");
//        param.put("is_appoint","");
//        param.put("appoint_type","");
//        param.put("expect_time","");
//        param.put("expect_pickup_time","");
//        param.put("lbs_type","");
//        param.put("is_insured","");
//        param.put("is_person_direct","");
//        param.put("vehicle","");
//        param.put("four_wheeler_type","");
//        param.put("declared_value","");
//        param.put("gratuity_fee","");
//        param.put("rider_pick_method","");
//        param.put("return_flag","");
        String jsonString = formatAndSortMap(param,0);
        String sign = generateOpenSign(jsonString,169xxx52,"3c58cb1exxxxxx867");
        System.out.println(sign);
    }

    /**
     * 生成签名
     * @param jsonString
     * @param devId
     * @param appKey
     * @return
     *
     */
    public static String generateOpenSign(String jsonString, Integer devId, String appKey) throws IOException {
        String sb = jsonString+"&" + devId + "&" + appKey;
        MessageDigest md = null;
        String ret = null;
        try {
            md = MessageDigest.getInstance("MD5");
            byte[] md5 = md.digest(sb.toString().getBytes("utf-8"));
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < md5.length; offset++) {
                i = md5[offset];
                if (i < 0) {
                    i += 256;
                }
                if (i < 16) {
                    buf.append("0");
                }
                buf.append(Integer.toHexString(i));
            }
            ret = Base64.encodeBase64String(buf.toString().getBytes("utf-8"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return  ret;
    }

    // 将 Map 转换成指定格式的字符串并排序键值对
    public static String formatAndSortMap(Map<String, Object> map, int indentLevel) {
        // 将 Map 的键值对转换成 List
        List<Map.Entry<String, Object>> entryList = new ArrayList<>(map.entrySet());

        // 对 List 中的键值对按照键进行排序
        Collections.sort(entryList, Comparator.comparing(Map.Entry::getKey));

        // 构建格式化后的字符串
        StringBuilder sb = new StringBuilder();
        String indent = getIndent(indentLevel);
        sb.append("{\n");
        for (Map.Entry<String, Object> entry : entryList) {
            sb.append(indent).append("  \"").append(entry.getKey()).append("\": ");
            Object value = entry.getValue();
            if (value instanceof Map) {
                // 如果值是 Map,则递归处理
                sb.append(formatAndSortMap((Map<String, Object>) value, indentLevel + 1));
            } else if (value instanceof List) {
                // 如果值是 List,则递归处理
                sb.append(formatList((List<?>) value, indentLevel + 1));
            } else if (value instanceof String) {
                sb.append("\"").append(value).append("\"");
            } else {
                sb.append(value);
            }
            sb.append(",\n");
        }
        sb.deleteCharAt(sb.length() - 2); // 删除最后一个逗号
        sb.append(indent).append("}");

        return sb.toString();
    }

    // 将 List 转换成指定格式的字符串
    private static String formatList(List<?> list, int indentLevel) {
        StringBuilder sb = new StringBuilder();
        String indent = getIndent(indentLevel);
        sb.append("[\n");
        for (Object value : list) {
            sb.append(indent).append("  ");
            if (value instanceof Map) {
                // 如果值是 Map,则递归处理
                sb.append(formatAndSortMap((Map<String, Object>) value, indentLevel + 1));
            } else if (value instanceof String) {
                sb.append("\"").append(value).append("\"");
            } else {
                sb.append(value);
            }
            sb.append(",\n");
        }
        sb.deleteCharAt(sb.length() - 2); // 删除最后一个逗号
        sb.append(indent).append("]");
        return sb.toString();
    }

    // 根据缩进级别生成缩进字符串
    private static String getIndent(int indentLevel) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < indentLevel; i++) {
            sb.append("  "); // 使用两个空格作为缩进
        }
        return sb.toString();
    }
}

三、回调

官方回调接口文档

前面的工作其实还好,我在开发回调这里没有注意到他们是有文档的,后面才发现这里可以自助操作,我当时是下生产订单(当然,这样是要你们给真实的钱出去的),然后真实的骑手过来取货,然后联系他们的人员,把你下的这订单指派给这位骑手小哥,我这里还的感谢那位骑手小哥,他坐在我旁边配合我测试,配合我联调,非常谢谢他

联调的话,你要再后台配置你的回调URL

以上动作配置好了之后,就可以下面操作 

package com.admin.business.interfaces.service.sfsamecity;


import cn.hutool.json.JSONObject;
import com.admin.frame.base.ConfigMapper;
import com.admin.util.BeanUtils;
import com.google.common.collect.ImmutableMap;
import com.itextpdf.text.log.Logger;
import com.itextpdf.text.log.LoggerFactory;
import org.apache.commons.lang.StringUtils;
import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import static javax.crypto.Cipher.SECRET_KEY;

@RestController
@RequestMapping("/sfsamecity")
public class SFSameCityBackController {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${sf.same.city.devId}")
    private String devId;
    @Value("${sf.same.city.secretKey}")
    private String secretKey;


    /**
     * https://openic.sf-express.com/open/api/docs/index#/apidoc
     * 配送状态更改回调
     *
     *
     * shop_id	string(64)	空	是	店铺ID
     * sf_order_id	string(64)	0	是	顺丰订单ID
     * shop_order_id	string(64)	0	是	商家订单ID
     * url_index	string	空	是	回调url前缀	rider_status
     * operator_name	string	空	是	配送员姓名
     * operator_phone	string	空	是	配送员电话
     * rider_lng	string	空	是	配送员位置经度
     * rider_lat	string	空	是	配送员位置纬度
     * order_status	int	空	是	订单状态	10-配送员接单/改派;12:配送员到店;15:配送员配送中
     * status_desc	string	空	是	状态描述	文案见上个字段的注释
     * push_time	int	空	是	状态变更时间
     *
     */
    @RequestMapping("/getSFCallbackOrderStatus")
    public Map<String,Object> getSFCallbackOrderStatus(HttpServletRequest request, HttpServletResponse response) throws IOException, JSONException {
        Map<String,Object> resMap = new HashMap<>();
        System.out.println("----------------------------顺丰同城配送状态更改-----------------------------------------");
        StringBuilder requestBody = new StringBuilder();
        try (BufferedReader reader = request.getReader()) {
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
        }
        // 解析JSON数据
        JSONObject json = new JSONObject(requestBody.toString());
        // 从JSON对象中获取参数
        String sign = request.getParameter("sign");
        Integer order_status = json.getInt("order_status");
        String sf_order_id = json.getStr("sf_order_id");

        String sign2 = validateSignature(jsonObject.toString());

        if (!sign2.equals(sign)) {
            System.out.println("..................sign签名错误..................");
            resMap.put("error_code",500);
            resMap.put("error_msg","error  sign签名错误");
            return resMap;
        }

        try {
                if(order_status==10){
                    //10-配送员接单
                    delivery.setSfSameCityStatusExpress(2);
                }else if(order_status==12){
                    //12:配送员到店
                    delivery.setSfSameCityStatusExpress(3);
                }else if(order_status==15){
                    //15:配送员配送中
					resMap.put("error_code",0);
					resMap.put("error_msg","success");
					return resMap;
				}
				
        }catch (Exception e){
            System.out.println("顺丰同城配送状态更改异常:");
            e.printStackTrace();
        }
        resMap.put("error_code",500);
        resMap.put("error_msg","error");
        return resMap;
    }




    private String validateSignature(String jsonString) throws IOException {
        return SFHapper.generateOpenSign(jsonString,Integer.parseInt(devId),secretKey);
    }

}

四、上线

前面的开发,联调,测试阶段结束了,终于等到上线了,这个时候由于他们流程原因,需要审核一段时间,是邮箱审核,具体看他们审核进度,跟我们说是要等一个星期,如果很急的话,可以提前跟他们讲,把材料给他们,把事情讲清楚,然后提前审批,不然等开发完了,上线还得再等一星期

这个是目前我对接他们的现状,希望他们做的越来越好,规范化,标准化,越做越强大

如果小伙伴们有什么疑问,欢迎下面评论。欢迎指正。如还有什么不懂的加我 QQ:517861659

如果没有及时回复,可以点我先问问AI机器人​编辑https://chatgpt.byabstudio.com/login?code=202307011314

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/557459.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习--CNN应用--VGG16网络和ResNet18网络

前言 我们在学习这两个网络时&#xff0c;应先了解CNN网络的相关知识 深度学习--CNN卷积神经网络&#xff08;附图&#xff09;-CSDN博客 这篇博客能够帮我们更好的理解VGG16和RetNet18 1.VGG16 1.1 VGG简介 VGG论文网址&#xff1a;VGG论文 大家有兴趣的可以去研读一下…

C++设计模式|创建型 4.建造者模式

1.什么是建造者模式? 建造者模式&#xff08;也被成为生成器模式&#xff09;&#xff0c;是一种创建型设计模式&#xff0c;软件开发过程中有的时候需要创建很复杂的对象&#xff0c;而建造者模式的主要思想是将对象的构建过程分为多个步骤&#xff0c;并为每个步骤定义一个…

共享WiFi贴推广有哪些好处?一文说清

随着科技发展&#xff0c;更加智能高效便捷的共享wifi贴开始越来越多的取代了传统的wifi连接方法。 因为传统的连接WiFi方式确实存在一些不便之处。客人需要手动搜索店铺的WiFi&#xff0c;然后输入复杂的密码进行连接。这一过程不仅繁琐&#xff0c;而且容易出错。有时&#…

稳了!麒麟信安云应用助力国产化生态平稳过渡

2016年国家提出了安全可控体系并大力推进基础硬件、基础软件及应用软件的国产化建设进程。各行业及企事业单位加大国产设备及软硬件的采购力度&#xff0c;逐步完成国产化转型。而操作系统作为上层应用的载体&#xff0c;在信息建设国产化中有着举足轻重的地位。 过去几十年&a…

虚函数求圆形、矩形面积

目录 题目 源码 结果示例 题目 写一个程序&#xff0c;定义抽象基类Shape&#xff0c;由它派生出2个派生类&#xff1a;Circle(圆形)、Rectangle(矩形)&#xff0c;用一个普通函数printarea分别输出以上二者的面积&#xff0c;2个图形的数据在定义对象时给定。 源码 #inc…

软考-系统集成项目管理中级--项目质量管理(输入输出很重要!!!本章占分较高,着重复习)

本章历年考题分值统计 本章重点常考知识点汇总清单 5、成本效益分析法:对每个质量活动进行成本效益分析&#xff0c;就是要比较其可能的成本与预期的效益。达到质量要求的主要效益包括减少返工、提高生产率、降低成本、提升干系人满意度及提升赢利能力。(掌握)17下64考题 本章…

【Flutter】GetX状态管理及路由管理用法

目录 一、安装二、使用1.安装GetX插件&#xff0c;快捷生成模版代码2.主入口MaterialApp改成GetMaterialApp3.定义路由常量RoutePath类、别名映射页面RoutePages类4. 初始initialRoute&#xff0c;getPages。5.调用 总结 一、安装 dependencies: get: ^4.6.6二、使用 1.安装G…

Java八股文4

Linux篇 1.free命令-查看内存状态 free命令用于显示内存状态&#xff0c;它可以提供关于系统内存使用情况的详细信息。这个命令会显示出内存的使用情况&#xff0c;包括实体内存、虚拟的交换文件内存、共享内存区段&#xff0c;以及系统核心使用的缓冲区等。 其中&#xff0c;参…

2024年国内USB Type-C厂商的机遇与挑战分析

USB Type-C接口作为一种全新的连接标准&#xff0c;已经在各种电子设备中得到了广泛的应用。2024年&#xff0c;国内USB Type-C厂商将面临着诸多机遇和挑战&#xff0c;需要全面分析和应对&#xff0c;以确保在竞争激烈的市场中保持竞争力和持续增长。 USB TYPE-C厂商在2024年…

09-ARM开发板的HelloWorld

在ARM开发板上运行x86_64平台程序 前面在Ubuntu系统编译生成了X86_64平台的HelloWorld程序&#xff0c;通过NFS服务器&#xff0c;尝试在开发板上直接运行。 如图所示&#xff0c;程序无法正常运行&#xff0c;终端提示ARM开发板在执行x86架构&#xff08;Intel或AMD&#xff…

【Linux】地址空间虚拟地址

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 虚拟地址1.1 虚拟地址引入1.2 虚拟地址理解1.3 虚拟地址细节问题 2. 地址空间2.1 理解地址空间2.2 页表和写时拷贝 3. 进程调度 1. 虚拟地址 1.1 虚拟地址引入 先先来一个测试代码&#xff1a; 1 #include<st…

Grass注册不了、按钮灰色的解决方案

近期相信grass挂机项目不少人有所有接触。还有不了解这个项目的可以看看博客&#xff1a; http://t.csdnimg.cn/bI4UO 但是不少人注册时遇到无法注册的问题&#xff0c;或者是注册按钮显示灰色&#xff0c;放上鼠标时显示禁止。这也是博主在尝试时遇到的问题。 经过探索&…

如何解决python安装mysqlclient失败问题

在使用Django等框架来操作MySQL时&#xff0c;实际上底层还是通过Python来操作的&#xff0c;首先需要安装一个驱动程序&#xff0c;在Python3中&#xff0c;驱动程序有多种选择&#xff0c;比如有pymysql以及mysqlclient等。使用pip命令安装mysqlclient失败应如何解决&#xf…

【Linux实践室】Linux高级用户管理实战指南:Linux用户与用户组编辑操作详解

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 &#x1f514;Linux查看用户属性命令2.1.1 &#x1f47b;…

测试用例的编写评审

1、什么叫软件测试用例 什么是测试用例 测试用例(TestCase) 是为项目需求而编制的一组测试输入、执行条件 以及预期结果&#xff0c;以便测试某个程序是否满足客户需求。–测试依据 可以总结为:每一个测试点的数据设计和步骤设计。–测试用例 2、测试用例的重要性(了解) 2.1…

社媒矩阵运营解决方案:海外云手机

在全球化的浪潮下&#xff0c;企业愈发认识到通过海外社交媒体平台扩大影响力、树立品牌形象及抢占国际市场的巨大机遇。因此&#xff0c;运营海外社交媒体账户已逐渐成为企业战略部署的重要组成部分。为了全面捕捉多渠道的流量&#xff0c;众多企业选择同时运营多个平台的多个…

基于Spring Boot的校园招聘系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

基于SkyEye运行Qt:著名应用程序开发框架

Qt是一个著名的跨平台的C图形用户界面应用程序开发框架&#xff0c;目前包括Qt Creator、Qt Designer等等快速开发工具&#xff0c;还支持2D/3D图形渲染、OpenGL&#xff0c;允许真正的组件编程&#xff0c;是与GTK、MFC、OWL、ATL一样的图形界面库。使用Qt开发的软件可以做到一…

抖音直播间没流量怎么办?如何快速提升直播间人气?

抖音直播间人气低迷&#xff0c;是否因为投入的资金不足或是数据表现不够抢眼而让你感到困惑&#xff1f;要提升抖音直播间的人气&#xff0c;首先需要深入了解抖音的推荐逻辑&#xff0c;探究直播间人气的真正来源。 抖音直播间的人气来源有哪些&#xff1f; 抖音直播间人气…

SpringMVC核心流程解析

SpringMVC核心流程解析 DispatcherServlet的继承关系请求流程分析获取HandlerChain(ControllrtMethod拦截器)获取HandlerAdapter handlerMappings的初始化过程 DispatcherServlet的继承关系 DispatcherServlet本质是一个servlet&#xff0c;既然是servlet&#xff0c;一个请求…
最新文章