项目

一般

简介

关于spring MVC重复调用的性能问题 » KmssRequestMappingHandlerMapping(1).java

qiu jk, 2022-08-10 06:27

 
1
package org.springframework.web.servlet.handler;
2

    
3
import org.apache.commons.lang3.exception.ExceptionUtils;
4
import org.springframework.util.ClassUtils;
5
import org.springframework.web.cors.CorsUtils;
6
import org.springframework.web.method.HandlerMethod;
7
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
8
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
9

    
10
import javax.servlet.http.HttpServletRequest;
11
import java.lang.reflect.Field;
12
import java.lang.reflect.Method;
13
import java.util.*;
14
import java.util.concurrent.CopyOnWriteArrayList;
15

    
16
/**
17
 * 覆盖RequestMappingHandlerMapping的实现,优化性能
18
 *
19
 * @author yezq
20
 */
21
public class KmssRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
22
    /**
23
     * 构造实例
24
     */
25
    public static RequestMappingHandlerMapping build() {
26
        return new KmssRequestMappingHandlerMapping();
27
    }
28

    
29
    private KmssMappingRegistry mappingRegistry;
30

    
31
    private List<RequestMappingInfo> patternsMappings = new CopyOnWriteArrayList<>();
32

    
33
    private KmssRequestMappingHandlerMapping() {
34
        // 重写mappingRegistry属性
35
        mappingRegistry = new KmssMappingRegistry();
36
        try {
37
            Field field = AbstractHandlerMethodMapping.class.getDeclaredField("mappingRegistry");
38
            field.setAccessible(true);
39
            field.set(this, mappingRegistry);
40
        } catch (Exception e) {
41
            ExceptionUtils.wrapAndThrow(e);
42
        }
43
    }
44

    
45
    /**
46
     * 覆盖MappingRegistry,记录带通配符的路径
47
     */
48
    private class KmssMappingRegistry extends AbstractHandlerMethodMapping<RequestMappingInfo>.MappingRegistry {
49
        @Override
50
        public void register(RequestMappingInfo mapping, Object handler, Method method) {
51
            super.register(mapping, handler, method);
52
            Set<String> paths = getMappingPathPatterns(mapping);
53
            if (paths.isEmpty()) {
54
                patternsMappings.add(mapping);
55
            }else {
56
                for (String path : paths) {
57
                    if (getPathMatcher().isPattern(path)) {
58
                        patternsMappings.add(mapping);
59
                        return;
60
                    }
61
                }
62
            }
63
        }
64

    
65
        @Override
66
        public void unregister(RequestMappingInfo mapping) {
67
            super.unregister(mapping);
68
            patternsMappings.remove(mapping);
69
        }
70
    }
71

    
72
    // ========== 以下代码主框架抄袭父类,修改地方见注释 ==========
73
    @Override
74
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
75
        List<Match> matches = new ArrayList<>();
76
        List<RequestMappingInfo> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
77
        if (directPathMatches != null) {
78
            addMatchingMappings(directPathMatches, matches, request);
79
        }
80
        if (matches.isEmpty()) {
81
            //原文:addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
82
            //原文的遍历太多,实际上只需要遍历带通配符的mapping就好
83
            addMatchingMappings(this.patternsMappings, matches, request);
84
        }
85

    
86
        if (!matches.isEmpty()) {
87
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
88
            matches.sort(comparator);
89
            Match bestMatch = matches.get(0);
90
            if (matches.size() > 1) {
91
                if (logger.isTraceEnabled()) {
92
                    logger.trace(matches.size() + " matching mappings: " + matches);
93
                }
94
                if (CorsUtils.isPreFlightRequest(request)) {
95
                    return PREFLIGHT_AMBIGUOUS_MATCH;
96
                }
97
                Match secondBestMatch = matches.get(1);
98
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
99
                    Method m1 = bestMatch.handlerMethod.getMethod();
100
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
101
                    String uri = request.getRequestURI();
102
                    throw new IllegalStateException(
103
                            "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
104
                }
105
            }
106
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
107
            handleMatch(bestMatch.mapping, lookupPath, request);
108
            return bestMatch.handlerMethod;
109
        } else {
110
            //原文:return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
111
            //原文很奇怪,明明找不到,handleNoMatch又遍历了一次,还是找不到
112
            return null;
113
        }
114
    }
115

    
116
    // ========== 以下代码纯搬运 ==========
117
    private static final HandlerMethod PREFLIGHT_AMBIGUOUS_MATCH = new HandlerMethod(new EmptyHandler(),
118
            ClassUtils.getMethod(EmptyHandler.class, "handle"));
119

    
120
    private void addMatchingMappings(Collection<RequestMappingInfo> mappings, List<Match> matches,
121
                                     HttpServletRequest request) {
122
        for (RequestMappingInfo mapping : mappings) {
123
            RequestMappingInfo match = getMatchingMapping(mapping, request);
124
            if (match != null) {
125
                matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
126
            }
127
        }
128
    }
129

    
130
    private class MatchComparator implements Comparator<Match> {
131
        private final Comparator<RequestMappingInfo> comparator;
132

    
133
        public MatchComparator(Comparator<RequestMappingInfo> comparator) {
134
            this.comparator = comparator;
135
        }
136

    
137
        @Override
138
        public int compare(Match match1, Match match2) {
139
            return this.comparator.compare(match1.mapping, match2.mapping);
140
        }
141
    }
142

    
143
    private class Match {
144
        private final RequestMappingInfo mapping;
145

    
146
        private final HandlerMethod handlerMethod;
147

    
148
        public Match(RequestMappingInfo mapping, HandlerMethod handlerMethod) {
149
            this.mapping = mapping;
150
            this.handlerMethod = handlerMethod;
151
        }
152

    
153
        @Override
154
        public String toString() {
155
            return this.mapping.toString();
156
        }
157
    }
158

    
159
    private static class EmptyHandler {
160
        @SuppressWarnings("unused")
161
        public void handle() {
162
            throw new UnsupportedOperationException("Not implemented");
163
        }
164
    }
165
}
(1-1/3)