村级网站建设,为什么原网站建设公司不愿意透露域名管理权限给客户,淘宝客一定要建立网站,东莞画册设计上一篇《【Spring6源码・MVC】初始化registry#xff0c;完成url和controller的映射关系》我们知道#xff0c;在IOC容器加载的同时#xff0c;初始化了registry这个HashMap#xff0c;这个HashMap中存放了请求路径和对应的方法。当我们请求进来#xff0c;会通过这个regi…上一篇《【Spring6源码・MVC】初始化registry完成url和controller的映射关系》我们知道在IOC容器加载的同时初始化了registry这个HashMap这个HashMap中存放了请求路径和对应的方法。当我们请求进来会通过这个registry去获取对应的方法。
在SpringBoot项目启动的时候会加载一个线程 步入run方法 步入run方法
当前这个类是WorkerClass Worker 主要维护运行任务的线程的中断控制状态以及其他次要簿记。此类适时地扩展了 AbstractQueuedSyncer以简化获取和释放围绕每个任务执行的锁。这可以防止旨在唤醒等待任务的工作线程而不是中断正在运行的任务的中断。我们实现了一个简单的非重入互斥锁而不是使用 ReentrantLock因为我们不希望工作线程任务在调用池控制方法如 setCorePoolSize时能够重新获取锁。此外为了在线程实际开始运行任务之前抑制中断我们将锁定状态初始化为负值并在启动时清除它在 runWorker 中。
这个run方法主要是启动主运行循环以此从队列中取出task执行。 当我们发送一个请求时http://localhost:8081/user/test
会调用这个runWorker方法中的task.run()
runWorker这个方法是主工作线程运行循环。反复从队列中获取任务并执行它们同时处理许多问题
我们可以从初始任务开始在这种情况下我们不需要获取第一个任务。否则只要池正在运行我们就从getTask获取任务。如果它返回 null则工作线程由于池状态或配置参数更改而退出。其他退出是由外部代码中的异常抛出引起的在这种情况下complete突然持有这通常会导致processWorkerExit替换此线程。在运行任何任务之前获取锁以防止在任务执行时出现其他池中断然后我们确保除非池停止否则该线程没有设置中断。每次任务运行之前都会调用 beforeExecute这可能会引发异常在这种情况下我们会导致线程死亡以 completeA 突然为 true 中断循环而不处理任务。假设 beforeExecute 正常完成我们运行任务收集其抛出的任何异常以发送到 afterExecute。我们分别处理 RuntimeException、Error规范保证我们捕获这两个错误和任意 Throwable。因为我们无法在 Runnable.run 中重新抛出 Throwable所以我们在出路时将它们包装在 Errors 中到线程的 UncaughtExceptionHandler。任何抛出的异常也保守地导致线程死亡。task.run 完成后我们调用 afterExecute这也可能会抛出异常这也会导致线程死亡。根据 JLS Sec 14.20即使 task.run 抛出此异常也会生效。异常机制的净效果是afterExecute 和线程的 UncaughtExceptionHandler 具有尽可能准确的信息我们可以提供有关用户代码遇到的任何问题的信息。 当我们发送http://localhost:8081/user/test请求时步入这个task.run()方法中 步入doRun方法中 … … …
最终经过数十个调用会在一个filterChanin链中调用servlet.service(request, response)方法 然后会判断当前请求中的方法是否包含规定的方法HTTP_SERVLET_METHODS.contains(request.getMethod()) 然后会调用doGet方法 之后调用经典的doService方法 步入doService方法之后又会调用doDispatch方法 看一看这个核心方法doDispatch。 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {...try {...try {// 检查是否文件请求processedRequest checkMultipart(request);...// 根据请求找到对应的控制器执行器链HandlerExecutionChain mappedHandler getHandler(processedRequest);...// 找到对应控制器的适配器用来执行操作的HandlerAdapter ha getHandlerAdapter(mappedHandler.getHandler());...// 执行拦截器前置if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 真正执行控制器逻辑mv ha.handle(processedRequest, response, mappedHandler.getHandler());...// 执行拦截器后置mappedHandler.applyPostHandle(processedRequest, response, mv);}...// 处理返回结果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}...
}先看checkMultipart(request)方法它是用来检查是否是文件上传如果有文件上传则将request包装成StandardMultipartHttpServletRequest
关于mappedHandler getHandler(processedRequest);和HandlerAdapter ha getHandlerAdapter(mappedHandler.getHandler());这两个方法和前面AOP讲到的获取拦截器和适配器的逻辑差不多循环去匹配这里就不展开了。
重点看一下mv ha.handle(processedRequest, response, mappedHandler.getHandler());这个真正执行控制器逻辑的方法。
步入handle方法最终来到这 步入invokeHandlerMethod方法
首先创建ServletInvocableHandlerMethod对象再去调用。 步入invokeAndHandle方法
首先去执行控制器的逻辑如果有结果再去处理结果。 看看如何执行控制器的逻辑的步入invokeForRequest方法
首先获取方法参数再去invoke
如何实现的就不看了吧最后肯定是调用本地方法去执行控制器的逻辑。
感兴趣可以翻一翻我的博客有一篇是如何解析本地方法的。可以看看cpp是如何实现的。 最后我们能得到返回的结果 最后会去调用这行代码
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);步入handleReturnValue方法 步入handleReturnValue方法 步入writeWithMessageConverters方法
最后经过层层包装调用((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);方法 步入write方法 步入writeInternal方法 步入copy方法
落叶归根结束。 其实关于SpringMVC还有很多没写但是首先知道这个流程就基本够用了之后用到哪些的哪些的时候再按着这个流程看就好。
值得一提的是我们这个demo没有文件也没有参数所以撸流程还是很容易的如果有参数还要注意是如何解析参数的如果用RequestParam注解的话直接通过反射就可以获取到Spring也提供了处理这个注解的解析器如果不加注解会默认使用名称绑定底层用asm框架读取字节码来获取参数名称所以编码记得用RequestParam声明参数之后会放进一个缓存数组中在ServletInvocableHandlerMethod invocableMethod createInvocableHandlerMethod(handlerMethod);代码执行时就会封装成ServletInvocableHandlerMethod 对象。
其他的暂且先不提了都比较简单点点看看就行了。