这篇文章给大家介绍SpringMVC中Controller的查找原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
SpringMVC请求流程

SpringMVC初始化过程
理解初始化过程之前,先认识两个类
RequestMappingInfo类,对RequestMapping注解封装。里面包含http请求头的相关信息。如uri、method、params、header等参数。一个对象对应一个RequestMapping注解
HandlerMethod类,是对Controller的处理请求方法的封装。里面包含了该方法所属的bean对象、该方法对应的method对象、该方法的参数等。

protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: ">
@Override
protected boolean isHandler(Class<?> beanType) {
return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
protected void detectHandlerMethods(final Object handler) {
//获取到当前Controller bean的class对象
Class<?> handlerType = (handler instanceof String) ?
getApplicationContext().getType((String) handler) : handler.getClass();
//同上,也是该Controller bean的class对象
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//获取当前bean的所有handler method。这里查找的依据便是根据method定义是否带有RequestMapping注解。如果有根据注解创建RequestMappingInfo对象
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
public boolean matches(Method method) {
return getMappingForMethod(method, userType) != null;
}
});
//遍历并注册当前bean的所有handler method
for (Method method : methods) {
T mapping = getMappingForMethod(method, userType);
//注册handler method,进入以下方法
registerHandlerMethod(handler, method, mapping);
}
}
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
//获取method的@RequestMapping注解
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
//获取method所属bean的@RequtestMapping注解
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
//合并两个@RequestMapping注解
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
//创建HandlerMethod
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
//检查配置是否存在歧义性
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean()
+ "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '"
+ oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
}
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
}
//获取@RequestMapping注解的value,然后添加value->RequestMappingInfo映射记录至urlMap中
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
}
这里T的类型是RequestMappingInfo。这个对象就是封装的具体Controller下的方法的RequestMapping注解的相关信息。一个RequestMapping注解对应一个RequestMappingInfo对象。HandlerMethod和RequestMappingInfo类似,是对Controlelr下具体处理方法的封装。先看方法的第一行,根据handler和mehthod创建HandlerMethod对象。第二行通过handlerMethods map来获取当前mapping对应的HandlerMethod。然后判断是否存在相同的RequestMapping配置。如下这种配置就会导致此处抛
Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map...
异常
@Controller
@RequestMapping("/AmbiguousTest")
public class AmbiguousTestController {
@RequestMapping(value = "/test1")
@ResponseBody
public String test1(){
return "method test1";
}
@RequestMapping(value = "/test1")
@ResponseBody
public String test2(){
return "method test2";
}
}
registerHandlerMethod方法简单总结
该方法的主要有3个职责
检查RequestMapping注解配置是否有歧义。
构建RequestMappingInfo到HandlerMethod的映射map。该map便是AbstractHandlerMethodMapping的成员变量handlerMethods。LinkedHashMap<RequestMappingInfo,HandlerMethod>。
构建AbstractHandlerMethodMapping的成员变量urlMap,MultiValueMap<String,RequestMappingInfo>。这个数据结构可以把它理解成Map<String,List>。其中String类型的key存放的是处理方法上RequestMapping注解的value。就是具体的uri
先有如下Controller
@Controller
@RequestMapping("/UrlMap")
public class UrlMapController {
@RequestMapping(value = "/test1", method = RequestMethod.GET)
@ResponseBody
public String test1(){
return "method test1";
}
@RequestMapping(value = "/test1")
@ResponseBody
public String test2(){
return "method test2";
}
@RequestMapping(value = "/test3")
@ResponseBody
public String test3(){
return "method test3";
}
}
@Controller
@RequestMapping("/LookupTest")
public class LookupTestController {
@RequestMapping(value = "/test1", method = RequestMethod.GET)
@ResponseBody
public String test1(){
return "method test1";
}
@RequestMapping(value = "/test1", headers = "Referer=https://www.baidu.com")
@ResponseBody
public String test2(){
return "method test2";
}
@RequestMapping(value = "/test1", params = "id=1")
@ResponseBody
public String test3(){
return "method test3";
}
@RequestMapping(value = "/*")
@ResponseBody
public String test4(){
return "method test4";
}
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//根据uri获取直接匹配的RequestMappingInfos
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
//不存在直接匹配的RequetMappingInfo,遍历所有RequestMappingInfo
if (matches.isEmpty()) {
// No choice but to go through all mappings
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
}
//获取最佳匹配的RequestMappingInfo对应的HandlerMethod
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found ">
进入lookupHandlerMethod方法,其中lookupPath="/LookupTest/test1",根据lookupPath,也就是请求的uri。直接查找urlMap,获取直接匹配的RequestMappingInfo list。这里会匹配到3个RequestMappingInfo。如下

然后进入addMatchingMappings方法
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, handlerMethods.get(mapping)));
}
}
}
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
int result = patternsCondition.compareTo(other.getPatternsCondition(), request);
if (result != 0) {
return result;
}
result = paramsCondition.compareTo(other.getParamsCondition(), request);
if (result != 0) {
return result;
}
result = headersCondition.compareTo(other.getHeadersCondition(), request);
if (result != 0) {
return result;
}
result = consumesCondition.compareTo(other.getConsumesCondition(), request);
if (result != 0) {
return result;
}
result = producesCondition.compareTo(other.getProducesCondition(), request);
if (result != 0) {
return result;
}
result = methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
result = customConditionHolder.compareTo(other.customConditionHolder, request);
if (result != 0) {
return result;
}
return 0;
}
@RequestMapping(value = "/test5", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String test5(){
return "method test5";
}
@RequestMapping(value = "/test5", method = {RequestMethod.GET, RequestMethod.DELETE})
@ResponseBody
public String test6(){
return "method test6";
}
public int compareTo(RequestMethodsRequestCondition other, HttpServletRequest request) {
return other.methods.size() - this.methods.size();
}
关于SpringMVC中Controller的查找原理是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。