Affects: \
Due to business needs, we need to support dynamic registration or offline RequestMappingInfo, so we implement CustomRequestMappingHandlerMapping based on RequestMappingHandlerMapping like this.
@Component
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private Map<String, MappingInfoCacheVO> cache = new HashMap<>();
@Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
if(handler instanceof String){
String beanName = (String) handler;
String name = method.getName();
cache.put(String.join("#", beanName, name), new MappingInfoCacheVO(handler, method, mapping));
}
}
public MappingInfoCacheVO getCacheMappingInfo(String key){
return cache.get(key);
}
static class MappingInfoCacheVO {
Object handler;
Method method;
RequestMappingInfo mapping;
public MappingInfoCacheVO(Object handler, Method method, RequestMappingInfo mapping) {
this.handler = handler;
this.method = method;
this.mapping = mapping;
}
}
}
Then, we'll inject this cacheMapping into the controller so that we can use it in the request.
@Autowired
private CustomRequestMappingHandlerMapping mappingValue;
@PostMapping("/register")
public MappingInfoCacheVO register(@RequestParam String key){
MappingInfoCacheVO reqVO = mappingValue.getCacheMappingInfo(key);
mappingValue.registerMapping(reqVO.getMapping(), reqVO.getHandler(), reqVO.getMethod());
return reqVO;
}
However, when I successfully registered, I got an Ambiguous exception when I made an HTTP request. Then I trying debug and found this define in the AbstractHandlerMethodMapping class. org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register
public void register(T mapping, Object handler, Method method) {
// Omit some of the content
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
// Omit some of the content
this.registry.put(mapping,
new AbstractHandlerMethodMapping.MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
// Omit some of the content
}
The method validateMethodMapping called here does not check whether the same mapping is registered. As a result, the same mapping information is registered in org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#pathLookup. Then an ambiguous exception occurs in the HTTP request .
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// Omit some of the content
if (!matches.isEmpty()) {
AbstractHandlerMethodMapping.Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
// Omit some of the content
}
else {
AbstractHandlerMethodMapping.Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
}
return bestMatch.getHandlerMethod();
}
}
So I wonder if this check should include the same mapping.
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#validateMethodMapping
Comment From: rstoyanchev
It's not clear what the goal is. Why does a mapping need to be registered twice?
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: spring-projects-issues
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.