[转] 公司使用SpringMVC的RESTful接口的坑

原文出处:https://tech.imdada.cn/2015/12/23/springmvc-restful-optimize/

这边文章的根源可以理解成Spring是何如路径解析的

背景:在公司还没有服务化的时候,在整个DB层上架构了一个系统作为数据访问层(封装业务系统对数据层的调用,实现对数据库的分库分表,实现对数据的缓存)对外提供的是RESTful接口

RESTful:

@RequestMapping(path = "/list/cityId/{cityId}", method = RequestMethod.GET)
@ResponseBody
public String getJsonByCityId(@PathVariable Integer cityId)

客户端请求: GET /list/cityId/1

非RESTful

@RequestMapping(path = "/list/cityId", method = RequestMethod.GET)
@ResponseBody
public String getJsonByCityId(@RequestParam Integer cityId)

客户端请求: GET /list/cityId?cityId=1

在接口越来越多的情况下,系统的性能在降低

从结果上可以看出,URL的数量越多,SpringMVC的性能越差,查看源码,依然从DispatcherServlet#doDispatch方法入手

(1)AbstractHandlerMapping #getHandler
(2)AbstractHandlerMethodMapping #getHandlerInternal
(3)AbstractHandlerMethodMapping #lookupHandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<Match>();
        // 通过lookupPath获取匹配到的path
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
                //  非restful的接口,会匹配到并且添加到matches
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            // restful的接口,会匹配所有的URL (这就是根源所在,数量越多,性能越差)
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        if (!matches.isEmpty()) {
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            Collections.sort(matches, comparator);

                    // 选取排序后的第一个作为最近排序条件
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                Match secondBestMatch = matches.get(1);
                // 前两个匹配条件排序一样抛出异常
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
                            request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
                }
            }
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }

SpringMVC首先对HTTP请求中的path与已注册的RequestMappingInfo(经解析的@RequestMapping)中的path进行一个完全匹配来查找对应的HandlerMethod,即处理该请求的方法,这个匹配就是一个Map#get方法。若找不到则会遍历所有的RequestMappingInfo进行查找。这个查找是不会提前停止的,直到遍历完全部的RequestMappingInfo

springMVC的匹配逻辑:

在遍历的过程中,SpringMVC首先会根据@RequestMapping的headers,params,produces,consumes,methods与实际的HttpServletRequest中的信息对比,剔除一些明显不合格的RequestMapping,如果以上信息都能匹配上,那么就会对path进行正则匹配,进行打分

public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
     RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
     ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
     HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
     ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
     ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);

     if (methods == null || params == null || headers == null || consumes == null || produces == null) {
        return null;
      }

      PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
      if (patterns == null) {
        return null;
      }

      RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
        if (custom == null) {
        return null;
      }

      return new RequestMappingInfo(this.name, patterns,methods, params, headers, consumes, produces, custom.getCondition());
}

Comparator comparator = new MatchComparator(getMappingComparator(request))

String match = getMatchingPattern(pattern, lookupPath);

path的匹配首先会把url按照“/”分割,然后对于每一部分都会使用到正则表达式,即使该字符串是定长的静态的。所以该匹配逻辑的性能可能会很差

至于解决方案,公司的文章里面有写到,我正好趁这次机会把原理重新熟悉了一遍

时间: 2024-10-02 19:26:26

[转] 公司使用SpringMVC的RESTful接口的坑的相关文章

广告-公司让写客户端首页接口,不明白什么意思?

问题描述 公司让写客户端首页接口,不明白什么意思? 大神给讲一下,只知道普通的接口的定义,服务接口还真不知道怎么下手.下面是原先一个小模块的服务接口 package cn.damai.mt.app.advert.rest.impl; import java.util.HashMap; import java.util.Map; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; impor

PHP编写RESTful接口_php实例

首先我们来认识下RESTful Restful是一种设计风格而不是标准,比如一个接口原本是这样的: http://www.test.com/user/view/id/1 表示获取id为1的用户信息,如果使用Restful风格,可以变成这样: http://www.test.com/user/1 可以很明显的看出这样做的好处: 1.更简洁的URL,对程序员友好 2.不暴露内部代码结构,更安全 那么,如何实现这个接口呢?首先,我们需要接收到/user/1部分. $path = $_SERVER['P

PHP编写RESTful接口的方法_php实例

这是一个轻量级框架,专为快速开发RESTful接口而设计.如果你和我一样,厌倦了使用传统的MVC框架编写微服务或者前后端分离的API接口,受不了为了一个简单接口而做的很多多余的coding(和CTRL-C/CTRL-V),那么,你肯定会喜欢这个框架! 先举个栗子1.写个HelloWorld.php,放到框架指定的目录下(默认是和index.php同级的apis/目录) /** * @path("/hw") */ class HelloWorld { /** * @route({&quo

使用IBM ODM V8.5中新增的RESTful接口的方法

本文将简要介绍 HTDS,讨论构建一个 HTDS 客户端和使用 IBM ODM V8.5 中新增的 http://www.aliyun.com/zixun/aggregation/14172.html">RESTful 接口的方法.本文将 重点介绍使用 HTDS 的潜在问题和难点,这些问题和难点主要与控制它的服务接口相关,本文还会介绍一些解决这些问题的实用方法. Hosted Transparent Decision Service (HTDS) 是一个部署到应用服务器上的预定义的应用程序

Ajax调用restful接口传送Json格式数据的方法_AJAX相关

ajax传送json格式数据,关键是指定contentType,data要是json格式 如果是restful接口,把type改成对应的post(增).delete(删).put(改).get(查)即可 var post_data={"name":"test001","pass":"xxxx"}; $.ajax({ url: "http://192.168.10.111:8080/uc/login", ty

Ajax调用restful接口传送Json格式数据的方法

ajax传送json格式数据,关键是指定contentType,data要是json格式 如果是restful接口,把type改成对应的post(增).delete(删).put(改).get(查)即可 var post_data={"name":"test001","pass":"xxxx"}; $.ajax({ url: "http://192.168.10.111:8080/uc/login", ty

springmvc+mybatis+restful+webservice Jeesz分布式架构

框架简介--主要定位于互联网企业架构,已内置企业信息化系统的基础功能和高效的代码生成工具,包括:系统权限组件.数据权限组件.数据字典组件.核心工具 组件.视图操作组件.工作流组件组件.代码生成等.采用分层设计.双重验证.提交数据安全编码.密码加密.访问验证.数据权限验证.平台简介         是一个分布式的框架,提供项目模块化.服务化.热插拔的思想,高度封装安全性的Java EE快速开发平台.         本身集成Dubbo服务管控.Zookeeper注册中心.Redis分布式缓存技术.

【SpringMVC整合MyBatis】springmvc对RESTful支持

1.什么是RESTful RESTful架构,就是目前最流行的一种互联网软件架构.它结构清晰.符合标准.易于理解.扩展方便,所以正得到越来越多网站的采用. RESTful-表现层状态转换(即Representational State Transfer的缩写)其实是一个开发理念,是对http的很好的诠释. (1)对url进行规范,写RESTful格式的url 非REST的url:http://...../queryItems.action?id=001&type=T01 REST的url风格:h

存储初创企业Versity公司提供S3对象存储接口

大量数字化处理对象. Versity公司是一家采用多线程SAM-QFS的归档软件初创企业. 这家年轻的公司成立于2011年3月,CEO Bruce Gilpin拥有风险投资背景,而CTO则为Harriet Coverston.她自1986年起即在LSC(即Large Storage Configurations)公司工作并担任技术负责人职务,而该公司正是QFS(即快速文件系统)的开发方. QFS能够对磁盘驱动器进行分组,并为其提供一套文件系统.该软件后被Sun公司收购,Coverston亦在工作