Spring中请求如何映射到达controller具体的方法

在这里插入图片描述
上图中DispatcherServlet的位置是:spring-webmvc依赖包(getHandler、doDispatch等方法都在这里)
在这里插入图片描述

SpringMVC的执行流程:

 - 用户发送请求至前端控制器DispatcherServlet。
 - DispatcherServlet收到请求调用HandlerMapping处理器映射器。
 - 处理器映射器找到具体的处理器(controller或者handle)(可以根据xml配置、注解进行查找),生成   - 处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
 - DispatcherServlet调用HandlerAdapter处理器适配器。
 - HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
 - Controller执行完成返回ModelAndView。
 - HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
 - DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
 - ViewReslover解析后返回具体View。
 - DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
 - DispatcherServlet响应用户。

名词解释:

 - 前端控制器(DispatcherServlet):接收请求,响应结果,相当于电脑的CPU。

 - 处理器映射器(HandlerMapping):根据URL去查找处理器

 - 处理器(Handler):(需要程序员去写代码处理逻辑的),即后端控制器用controller表示。

 - 处理器适配器(HandlerAdapter):会把处理器包装成适配器,这样就可以支持多种类型的处理器,类比笔记本的适配器(适配器模式的应用)

 - 视图解析器(ViewResovler):进行视图解析,多返回的字符串,进行处理,可以解析成对应的页面

Handle是什么?

Handler是一个Controller的对象和请求方式的组合的一个Object对象
HandleExcutionChains是HandleMapping返回的一个处理执行链,它是对Handle的二次封装,将拦截器关联到一起。然后,在DispatcherServlert中完成了拦截器链对handler的过滤。
DispatcherServlet要将一个请求交给哪个特定的Controller,它需要咨询一个Bean——这个Bean的名字为“HandlerMapping”。HandlerMapping是把一个URL指定到一个Controller上,(就像应用系统的web.xml文件使用<servlet-mapping>将URL映射到servlet)。

在SpringBoot项目中,我们通常在相应的controller中定义我们的方法。

@RestController
@RequestMapping("/api/{edition}/page")
public class MyPageHelperController {

    @Autowired
    private Environment env;

    @Autowired
    private PersonConfig personConfig;

    @GetMapping("/myPage")
    @ResponseBody
    public List<String> myPage(Object str) throws IOException {
        List<String> person= personConfig.getList(str);
        return person;
    }
}

那么当我们通过postman调用这个方法时,springboot是如何将请求分发到我们相应的方法的呢?下面就来简单的分析下。

DispatcherServlet 类
DispatcherServlet 译为分发器,其作用如其名,是 Spring 接收请求的中心调度类,此类就是专门负责将web请求分派给已注册的合适的处理程序处理Web请求
DispatcherServlet继承于FrameworkServlet
FrameworkServlet继承于HttpServletBean
HttpServletBean继承于HttpServlet

在这里插入图片描述
我们由postmsn发送的请求一般包括一下几种:

GET
POST
PUT
DELETE
OPTIONS
每一种请求都会调用一种对应的方法来处理,比如GET请求,就会由doGet()方法进行处理。而doGet方法被定义在了HttpServlet抽象类中
在这里插入图片描述
通关对继承树的观察,我们在HttpServlet的子类FrameworkServlet中发现了对doGet()方法的重写


	/**
	 * Delegate GET requests to processRequest/doService.
	 * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
	 * with a {@code NoBodyResponse} that just captures the content length.
	 * @see #doService
	 * @see #doHead
	 */
	@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

重写后的doGet方法实际上调用了另外一个方法processRequest
进入到这个方法中

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		// 省略部分代码

		try {
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

/**
	 * Process this request, publishing an event regardless of the outcome.
	 * <p>The actual event handling is performed by the abstract
	 * {@link #doService} template method.
	 */

通过该方法的注释,可以了解到processRequest此方法最终处理请求事件的处理器,是该类的抽象方法doService,其实现方法写在了子类DispatcherServlet中。

在DispatcherServlet找到对doService的实现方法,观察到其注释告诉我们,他将特定的属性和请求方法发送给doDispatch方法进行实际的调度。

	/**
	 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
	 * for the actual dispatching.
	 */

继续找到doDispatch方法,先看其注释

   /**
    * Process the actual dispatching to the handler.
    * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
    * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
    * to find the first that supports the handler class.
    * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
    * themselves to decide which methods are acceptable.
    * @param request current HTTP request
    * @param response current HTTP response
    * @throws Exception in case of any kind of processing failure
    */

注释说明,实际上是这个方法对请求做实际的分派,通过按顺序查找注册的HandlerMappings映射,获取查询到的第一个处理器。并且所有的 HTTP 方法都是由此方法做处理的

通过阅读doDispatch 方法,可以在其方法内找到一行注释以及一个方法:
在这里插入图片描述
这行注释告诉我们是getHandler这个方法来确定当前请求的处理器

进入getHandler方法内部


	/**
	 * Return the HandlerExecutionChain for this request.
	 * <p>Tries all handler mappings in order.
	 * @param request current HTTP request
	 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
	 */
	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

我们启动项目,请求一个hello测试接口
我们通过计算器查看handlerMapping,如下
在这里插入图片描述
可以看到有很多HandlerMapping,第一个(索引为0)的为RequestMappingHandlerMapping 就是和我们的请求息息相关的handlerMapping.
在进入RequestMappingHandlerMapping内部,如下图
在这里插入图片描述
直接找到mappingRegistry查看 如下图
在这里插入图片描述
就可以看到,里面维护了我们定义的所有的RequestMapping方法
而我此时请求的就是下面这个方法

@RestController
@RequestMapping("/api/{edition}/page")
public class MyPageHelperController {

    @Autowired
    private Environment env;

    @Autowired
    private PersonConfig personConfig;

    @GetMapping("/myPage")
    @ResponseBody
    public List<String> myPage(Object str) throws IOException {

        System.out.println(env.getProperty("api.url.default.message"));

        List<String> person= personConfig.getList();

        System.out.println(person);


        return person;
    }
}

当我们getHandler并传入当前request对象的时候,就可以看到如下图
在这里插入图片描述

此时已经成功找到了处理该请求的方法,然后就是后续调用该方法进行请求的处理。

注意:mappingRegistry中的数据是如何来的呢?

其实Spring Boot在启动后,会将所有扫描到的@RequestMapping注解注册到映射处理器handlerMappings中,其中包含了路径、方法等信息,在接收到请求时,会从注册的映射处理器中查找对应的路径方法,最后分发到方法中进行处理。

获取requestMappings示例代码:

@Component
public class MyMapping implements ApplicationContextAware {

    private Set<String> requestMappings = new HashSet<>(500);

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        AbstractHandlerMethodMapping<RequestMappingInfo> methodMapping = (AbstractHandlerMethodMapping<RequestMappingInfo>) applicationContext.getBean("requestMappingHandlerMapping");
        Map<RequestMappingInfo, HandlerMethod> mapRet = methodMapping.getHandlerMethods();
        mapRet.keySet().forEach(requestMappingInfo -> requestMappings.add(requestMappingInfo.toString()));
    }
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值