SpringMvc如何找到Controller

SpringMvc如何找到Controller?

最近一个朋友,碰到了这样一个问题:

为什么 SpringMvc 的必须定义为 Controller,它是如何找到的?

当定义为@Service时为什么找不到,当定义为@Service时,也想访问怎么办?

我这边首先贴出我找到的答案,然后给出我分析问题的思路

答案

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

正常形式

判断Spring的Bean必须是Controller注解

@Controller
public class IndexController {

    @RequestMapping("index")
    public void index(PrintWriter writer)  {
        writer.println("index page");
    }
}

访问 /index 页面输出 index page

强制加入

不是Controller注解时,加上RequestMapping注解也可以

@RequestMapping
@Service
public class IndexService {
@RequestMapping("service")
public void index(PrintWriter writer) {
writer.println("service page");
}
}

访问 /service 页面输出 service page

# 分析
分析之前假定以下条件
1. 会使用SpringMvc
2. 了解Servlet生命周期

要分析出为什么是Controller注解,那么首先在进行方法访问时,找到创建对象的(集合/Map),然后在去找使用集合的地方

## 逆向思维-Controller对象创建
在例子中的 IndexController->index 断点。 在调用堆栈中找到第一次使用IndexController类的地方

直到找到 DispatcherServlet->doDispatch 方法中的mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

在这里发现 mappedHandler.getHandler() 里面有一个bean 属性是IndexController

Controller被找到

也就是说创建 mappedHandler的时候,IndexController从集合中读取出来的

### mappedHandler创建
“`
//mappedHandler = getHandler(processedRequest);
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
“Testing handler map [” + hm + “] in DispatcherServlet with name ‘” + getServletName() + “’”);
}
//对象被找到
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}

### 跟进hm.getHandler(request);

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//第一行 对象被找到
Object handler = getHandlerInternal(request);
//省略无关代码…
}

### 跟进getHandlerInternal
这是一个抽象方法,需要重启服务器,然后debug步进

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug(“Looking up handler method for path ” + lookupPath);
}
this.mappingRegistry.acquireReadLock();
try {
// 找到HandlerMethod对象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug(“Returning handler method [” + handlerMethod + “]”);
}
else {
logger.debug(“Did not find handler method for [” + lookupPath + “]”);
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}

### 跟进lookupHandlerMethod

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// 因为版面原因,省略了很多代码,实际上内容并不是这样的
List matches = new ArrayList<>();
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
Match bestMatch = matches.get(0);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;

        // 实际上就是返回 this.mappingRegistry.getMappings() 中的某一个
}

找到了IndexController对象所属的集合: `org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getMappings()`

##  逆向思维-映射注册中心的注册拦截.
public Map<T, HandlerMethod> getMappings() {
        return this.mappingLookup;
    }

查找所有使用mappingLookup的方法

// 类 AbstractHandlerMethodMapping.MappingRegistry 的 register
public void register(T mapping, Object handler, Method method) {
// 因为版面原因,省略了很多代码,实际上内容并不是这样的
this.mappingLookup.put(mapping, handlerMethod);
}

然后到 `AbstractHandlerMethodMapping.MappingRegistry.register`断点, 重启服务器之后,进行堆栈分析


### 加载Bean相关方法
第二层堆栈 AbstractHandlerMethodMapping.detectHandlerMethods()

protected void detectHandlerMethods(final Object handler) {
Class


加载方法不属于本节讨论的主题,不做详细说明

### 加载isHandler()==true的Bean
在`AbstractHandlerMethodMapping.MappingRegistry.register()`下断点,然后重启服务器

第三层堆栈 AbstractHandlerMethodMapping.initHandlerMethods()
//AbstractHandlerMethodMapping 的 initHandlerMethods 方法
protected void initHandlerMethods() {
// 因为版面原因,省略了很多代码,实际上内容并不是这样的
//isHandler 是抽象方法,实际上执行 RequestMappingHandlerMapping 的 isHandler
    if (beanType != null && isHandler(beanType)) {
            detectHandlerMethods(beanName);
        }
}
// 类 RequestMappingHandlerMapping 的 isHandler
@Override
protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

“`

直到这里才终于找到我们想要了解的内容: SpringMvc 为什么只找Controller注解,而不找其他的

总结

在我们了解 SpringMvc 的全貌时,自然不用这种逆推的手段来了解我们想要的内容

但是当我们想要了解具体某个东西的时候,可以猜测它的实现,再去逆向分析就可以快速的得到我们想要的结果

猜你喜欢

转载自blog.csdn.net/mz4138/article/details/81417327