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 的全貌时,自然不用这种逆推的手段来了解我们想要的内容

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 如果在使用Spring MVC访问controller时遇到404错误,通常是因为controller没有正确映射到请求路径。确保使用了@RequestMapping或@GetMapping注解来映射请求路径,并且路径是正确的。还要检查是否在Spring配置文件中正确配置了路径映射。 ### 回答2: Spring MVC框架是一种基于MVC设计模式的Java Web应用框架,可以快速开发Web应用程序。在使用Spring MVC框架开发Web应用过程中,有时候会发现在访问Controller的时候会出现404的错误。 出现这种问题的原因一般有以下几个方面: 1. URL映射错误:在Spring MVC框架中,Controller的URL映射是根据@RequestMapping注解来完成的。如果URL映射设置错误或者Controller没有正确的映射,就会出现404错误。 2. Controller没有在Spring MVC配置文件中进行配置:当我们创建一个Controller类的时候,需要将该类注册到Spring MVC框架中。如果没有在Spring MVC配置文件中进行配置,就会出现404错误。 3. 没有编写视图页面:当Controller找到了相应的处理方法并经过处理之后,需要将结果返回给浏览器。这个结果一般是一个视图页面。如果没有编写相应的视图页面,就会出现404错误。 解决上述问题的方法如下: 1. 检查URL映射是否正确:需要检查Controller的@RequestMapping注解设置是否正确,并且需要注意大小写。如果发现设置有误,需要进行修改。 2. 将Controller注册到Spring MVC框架:需要在Spring MVC配置文件中将Controller进行配置,例如可以使用@ComponentScan注解进行扫描并注册。 3. 编写相应的视图页面:需要对于每一个Controller的处理方法都编写相应的视图页面,并且可以根据业务逻辑进行模板化编写,提高代码的复用性。 总之,出现Spring MVC访问Controller 404错误的原因一般有很多种,需要根据具体情况进行排查,找到问题的根源,并采取相应的解决措施。只有理解了错误的原因,才能够顺利地解决问题。 ### 回答3: Spring MVC框架是一种开源的、基于MVC(Model-View-Controller)模型的Web框架,它在Java EE平台上提供了一种优雅而灵活的方式,帮助我们构建Web应用程序。在使用Spring MVC框架时,我们希望访问的处理器(Controller)可以被正确的映射,并正确的处理请求。但有时候会遇到Spring MVC访问Controller出现404错误的情况,接下来我们来分析一下Spring MVC访问Controller404的解决办法。 1.配置错误:我们在Spring MVC的配置文件中,可能会出现一些配置错误,如Controller类路径或方法名错误、请求路径配置错误等导致404错误。这时候我们需要仔细查看配置文件,确认配置是否正确,如在RequestMapping注解中路径拼写错误等。 2.包名与访问路径不匹配:在Spring MVC中,只有请求地址正确与Controller类的包名匹配时,才能访问到正确的Controller。如果Controller类的包名与请求地址不匹配,就会出现404错误。解决方法可以在Controller类上使用RequestMapping注解,指定正确的请求路径。 3.缺少必要的组件:当我们在执行Spring MVC项目时,需要一些必要的组件来支持此框架的运行,如JSP/HTML,Tomcat等,如果这些组件缺失或者配置错误,就会出现404错误。 4.访问路径有误:我们在访问Controller时,可能会出现访问路径出错的情况,要检查访问的URL地址是否正确,是否携带了必要的参数等。 总之,在解决Spring MVC访问Controller 404错误时,需要先仔细检查代码、配置等问题,逐一排查,找到具体原因,然后进行针对性的修复。同时也需要对错误有一个良好的分析能力,找到根源。只有这样,才能顺利的运行一个Spring MVC框架项目。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值