SpringMvc的运行流程你真的懂了吗?
2021-04-24 07:29
标签:determine gets win 处理流程 后台 查找 exp request对象 isa ? 模型-视图-控制器(MVC)是一个众所周知的以设计界面应用程序为基础的设计思想。它主要通过分离模型、视图及控制器在应用程序中的角色将业务逻辑从界面中解耦。通常,模型负责封装应用程序数据在视图层展示。视图仅仅只是展示这些数据,不包含任何业务逻辑。控制器负责接收来自用户的请求,并调用后台服务(service或者dao)来处理业务逻辑。处理后,后台业务层可能会返回了一些数据在视图层展示。控制器收集这些数据及准备模型在视图层展示。MVC模式的核心思想是将业务逻辑从界面中分离出来,允许它们单独改变而不会相互影响。 Spring MVC框架也是一个基于请求驱动的Web框架,并且使用了前端控制器模式(是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理来进行设计,再根据请求映射规则分发给相应的页面控制器(动作/处理器)进行处理。首先让我们整体看一下Spring MVC处理请求的流程: 首先用户发送请求,请求被SpringMvc前端控制器(DispatherServlet)捕获; 前端控制器(DispatherServlet)对请求URL解析获取请求URI,根据URI, 调用HandlerMapping; 前端控制器(DispatherServlet)获得返回的HandlerExecutionChain(包括Handler对象以及Handler对象对应的拦截器); DispatcherServlet 根据获得的HandlerExecutionChain,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法); HandlerAdapter根据请求的Handler适配并执行对应的Handler;HandlerAdapter(提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据配置,Spring将做一些额外的工作: HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等数据格式化: 数据格式化。 如将字符串转换成格式化数字或格式化日期等 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中) Handler执行完毕,返回一个ModelAndView(即模型和视图)给HandlerAdaptor HandlerAdaptor适配器将执行结果ModelAndView返回给前端控制器。 前端控制器接收到ModelAndView后,请求对应的视图解析器。 视图解析器解析ModelAndView后返回对应View; 11、最终前端控制器将渲染后的页面响应给用户或客户端 ? 对于SpringMvc 项目所有的请求入口(静态资源除外)这里都是从web.xml文件配置的前端控制器DispatcherServlet开始, DispatcherServlet UML继承关系图如下: 这里关注蓝线部分继承结构:DispatcherServlet-->FrameworkServlet-->HttpServletBean-->HttpServlet-->GenericServlet-->Servlet,对于请求核心时序图如下: 对于web 请求的处理,大家都知道是通过继承HttpServlet重写其service方法,这里打开DispatcherServlet源码发现这里并没有看到我们要找的service方法,此时到父类FrameworkServlet 查找如下:可以看到父类重写HttpServlet service方法。 ? 从源码分析来看当请求方法为patch请求或者为null时执行proce***equest 方法,其他情况则调用父类service 方法,大家都知道SpringMvc 请求大多请求是get|post请求为主,此时继续向上查看FrameworkServlet 父类HttpServletBean(抽象类继承HttpServlet 并未重写service方法 所以向上继续寻找)-->HttpServlet service 方法 可以看到HttpServlet service 进行了重载,根据不同的请求类型然后调用不同处理方法,这里以get请求为例,当请求方法为get 请求时在重载service 方法中调用doGet 方法进行处理,这里需要特别注意的是:HttpServlet 存在doGet方法实现,然而在继承的子类中也存在doGet方法实现,到底调用哪个方法?很明显调用子类的doGet方法(面向对象多态思想!!!),从继承UML关系图上看,最外层子类实现doGet方法的为FrameworkServlet ? 该方法大概做了这几件事:国际化的设置,创建ServletRequestAttributes对象,初始化上下文holders(即将Request对象放入到线程上下文中,如后续想要在方法中获取request,response对象此时可以通过调用LocaleContextHolder对应方法即可),然后调用doService方法。对于doService方法,FrameworkServlet 类并未提供实现,该方法由DispatcherServlet子类实现 DispatcherServlet里面执行处理的入口方法是doService,由于这个类继承于FrameworkServlet类,重写了doService()方法 ? 整个方法看下来处理的操作有:处理include标签的请求,将上下文放到request的属性中,将国际化解析器放到request的属性中,将主题解析器放到request属性中,将主题放到request的属性中,处理重定向的请求数据最后调用doDispatch这个核心的方法对请求进行处理。 DispatcherServlet#doDispatch 该方法是在doService方法中调用的,从底层设计了整个请求的处理流程: render 视图渲染 业务逻辑 @MyController
* 创建控制器
*/
public class MyDispatcherServlet extends HttpServlet {
//创建ioC 创建 handler存放容器
private HashMap 如果需要更多资料:1080355292(进群暗号:99) SpringMvc的运行流程你真的懂了吗? 标签:determine gets win 处理流程 后台 查找 exp request对象 isa 原文地址:https://blog.51cto.com/14865341/2509308SpringMvc 请求流程
SpringMvc 请求执行源码解读
FrameworkServlet #service
/**
* Override the parent class implementation in order to intercept PATCH requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
proce***equest(request, response);
}
else {
super.service(request, response);
}
}
HttpServlet#service
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn‘t support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince
FrameworkServlet #doGet&proce***equest
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
proce***equest(request, response);
}
protected final void proce***equest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 系统计时开始时间
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 国际化
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
//构建ServletRequestAttributes对象
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
//异步管理
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//初始化ContextHolders
initContextHolders(request, localeContext, requestAttributes);
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 {
//恢复原来的LocaleContext和ServiceRequestAttributes到LocaleContextHolder和RequestContextHolder,避免影响Servlet以外的处理,如Filter
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
//发布ServletRequestHandlerEvent消息,这个请求是否执行成功都会发布消息的
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
// initContextHolders(request, localeContext, requestAttributes);
private void initContextHolders(HttpServletRequest request,
@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
}
DispatcherServlet#doService
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 校验是否为上传请求 是上传请求执行解析 否则返回request
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 根据访问的Handler 返回指定对应的HandlerExecutionChain对象 这里从HandlerMapping 集合中查找 HandlerExecutionChain 对象包含Handler与拦截器HandlerInterceptor列表
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根据得到的Handler 获取对应的HandlerAdaptor对象
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理GET、HEAD请求的Last-Modified
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
//当数据没有更改时,就直接返回上次的数据,提高效率
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//执行Interceptor的preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 执行Handler 返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//如果需要异步处理,直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//当view为空时,根据request设置默认view,如Handler返回值为void
applyDefaultViewName(processedRequest, mv);
//执行相应Interceptor的postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we‘re processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//处理返回结果,包括处理异常、渲染页面,发出完成通知触发Interceptor的afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
1. doDispatcher首先检查是不是上传请求,如果是则将request转换为MultipartHttpServletRequest,并将multipartRequestParsed标志设置为true
? 3. 处理GET、HEAD请求的Last-Modified,这里主要判断Last-Modified值是否被修改来处理决定是否采用缓存数据。
? 4. 接下来依次调用相应的Interceptor的preHandle。执行拦截器拦截操作
? 5. 拦截器preHandle方法执行后,此时开始通过HandlerAdapter 适配对应的Handler 执行(这里才是真正要执行的Controller方法), Handler处理完请求后,如果需要异步处理则直接返回,如果不需要异步处理,当view为空时,设置默认view,然后执行相应的Interceptor的postHandle。
DispatcherServlet#processDispatchResult
processDispatchResult方法主要用来处理前面返回的结果,其中包括处理异常、渲染页面、触发Interceptor的afterCompletion方法三部分内容,处理的异常是在处理请求doDispatch方的过程中产生。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
// 如果请求过程中有异常抛出则处理异常
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
//执行页面渲染操作
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
// Handler请求处理完,触发Interceptor的afterCompletion
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name ‘" + mv.getViewName() +
"‘ in servlet with name ‘" + getServletName() + "‘");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name ‘" + getServletName() + "‘");
}
}
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 渲染页面处理
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
手写SpringMVC
Spring MVC 的实现流程:
客户端请求被 DispatcherServlet(前端控制器)接收。
->根据 HandlerMapping 映射到 Handler。
->生成 Handler 和 HandlerInterceptor(如果有则生成)。
->Handler 和 HandlerInterceptor 以 HandlerExecutionChain 的形式一并返回给 DispatcherServlet。
->DispatcherServlet 通过 HandlerAdapter 调用 Handler 的方法做业务逻辑处理。
->返回一个 ModelAndView 对象给 DispatcherServlet。
->DispatcherServlet 将获取的 ModelAndView 对象传给 ViewResolver 视图解析器,将逻辑视图解析成物理视图 View。
->ViewResolver 返回一个 View 给 DispatcherServlet。
->DispatcherServlet 根据 View 进行视图渲染(将模型数据填充到视图中)。
->DispatcherServlet 将渲染后的视图响应给客户端。
分析
HTTP 请求是通过注解找到对应的 Controller 对象…;
Controller 的 Method 也是通过注解与 HTTP 请求映射的;
使用map 当做 ioC 容器,完成储存所有参数与业务的class;
初始化工作完成,接下来处理 HTTP 请求,业务流程如下:
DispatcherServlet 接收请求,通过映射从 IoC 容器中获取对应的 Controller 对象;
根据映射获取 Controller 对象对应的 Method;
调用 Method,获取返回值;
将返回值传给视图解析器,返回物理视图;
完成页面跳转。
自定义注解
@MyRequestMapping/**
* 自定义 @RequestMapping 注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
String value() default "";
}
/**
* @auther SyntacticSugar
* @data 2018/11/13 0013下午 9:15
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
String value() default "";
}
定义一个核心控制器MyDispatcherServlet
/**
* @auther SyntacticSugar
* @data 2018/11/13 0013下午 9:20
*
自定义一个视图解析器,MyViewResolver
/**
* @auther SyntacticSugar
* @data 2018/11/13 0013下午 9:31
*
* 自定义视图解析器 MyViewResolver
*/
public class MyViewResolver {
private String prefix;
private String suffix;
// 目标资源路径
public String jspMapping(String value){
return this.prefix+value+this.suffix;
}
//setter getter
......
}
创建测试 TestController ,对自定义的springmvc 进行测试
/**
* @auther SyntacticSugar
* @data 2018/11/13 0013下午 10:47
*/
@MyController
@MyRequestMapping("/testController")
public class TestController {
@MyRequestMapping("/test")
public String test(){
System.out.println("执行test相关业务");
return "index";
}
}
springmvc.xml配置
上一篇:《算法竞赛进阶指南》0x35高斯消元与线性空间 异或空间
下一篇:Jmeter启动报错:Not able to find Java executable or version. Please check your Java installation