首页 文章资讯内容详情

SpringMVC是怎么工作的,SpringMVC的工作原理

2026-06-01 4 花语

本文内容纲要:

-介绍 -从一个项目开始 -Servlet是JavaWeb应用的基石 -DispatcherServlet是SpringMVC的核心 -处理HTTP请求 -GenericServlet -HttpServlet -HttpServletBean -FrameworkServlet -DispatcherServlet:统一请求处理 -doService()方法设置请求信息 -doDispatch()方法分发请求 -HandlerAdapter处理请求 -渲染视图

SpringWebMVC是怎么工作的,SpringMVC的原理,SpringMVC源码分析。

目录

介绍

从一个项目开始

Servlet是JavaWeb应用的基石

DispatcherServlet是SpringMVC的核心

处理HTTP请求

GenericServlet HttpServlet HttpServletBean FrameworkServlet

DispatcherServlet:统一请求处理

doService()方法设置请求信息 doDispatch()方法分发请求 HandlerAdapter处理请求 渲染视图

介绍

SpringWebMVC是SpringFramework中的一部分,当我们需要使用spring框架创建web应用的时候就需要引入springwebmvc。对于程序员来说,我们只需要增加@Controller,@RequestMapping注解然后,浏览器中的请求就会到达springweb应用。我们只需要在controller中编写相关逻辑即可。然而,请求是在哪里接收的?@Controller,@RequestMapping注解做了什么?本文我们来探讨一下。

从一个项目开始

本文假定你已经能熟练的使用springmvc。为了展开后续的讨论,假设我们新建了一个spring-mvc-demo的项目。并由此项目来展开讨论。项目中有一个控制器,代码如下:

@Controller @RequestMapping("/app") publicclassAppController{ @RequestMapping(method=RequestMethod.GET,value="/hello") public@ResponseBodyStringhello(HttpServletRequestrequest,Stringname){ return"Hello,"+name; } }

控制器写好之后,我们将项目打车war包,放入tomcat容器中,并使用8080端口启动tomcat,运行项目,然后在浏览器中输入http://localhost:8080/app/hello?name=world.

我们在浏览器中可以看到:Hello,world的输出。

我们先记住这个例子,下面我们带着一些疑问继续看,这个请求是怎么被接收到的?请求是怎么交给AppController处理的?

Servlet是JavaWeb应用的基石

当你在浏览器中输入http://loalhost:8080/,按下enter建,然后请求命中了服务器,这是怎么发生的?又如何从这个请求中得到浏览器中可见的页面?

本例中,我们给出的是一个简单的spring-mvc应用,并放入了tomcat中(springboot内嵌tomcat启动其实也是一样的)。Tomcat是一个servlet容器,这点我想每个Java程序员都十分清楚,我们在没有使用spring-mvc之前,就是使用servlet+jsp来开发web应用。

由于Tomcat是一个web容器,每一个发送给Tomcat服务器的HTTP请求自然会被一个JavaServlet处理。所以,SpringMvc必定有一个servlet,SpringWeb应用的入口必定是一个Servlet,这应该不难想到。

简单来说,Servlet是任何JavaWeb应用的核心组件(除非你不用servlet规范,比如你使用netty)。Servlet它是低层次的,并且不会像MVC那样强加于特定的编程模式。它只是可以让你写一个偶一个Servlet,一个HTTPServlet可以接受一个HTTP请求,然后处理它,并返回一个响应。

而springmvc就是使用了一个大的servlet,下面我们就来说这个大的servlet。

DispatcherServlet是SpringMVC的核心

上面我们已经提到Servlet是Javaweb应用的基石Spring应用入口必定是一个Servlet,这个Servlet其实就是DispatcherServlet。

作为WEB应用的开发人员,我们真正想做的是抽象出以下繁琐和模板化的任务,并专注于有用的业务逻辑:

映射一个HTTP请求到某个处理方法。 将HTTP请求数据,和头信息转换成数据对象(DTO/domainobject)。 模型-视图-控制器之间的交互。 从DTO,域对象等生成响应

SpringDispatcherServlet提供了这些。它是SpringWebMVC框架的核心,这个核心组件接收所有请求到您的应用程序。

DispatcherServlet具有很强的可扩展性。例如,它允许您插入不同的现有或新适配器以执行大量任务: 将请求映射到应该处理它的类或方法(HandlerMapping接口的实现) 使用特定模式处理请求,例如常规servlet,更复杂的MVC工作流或者POJObean中的方法(HandlerAdapter接口的实现) 通过名称解析视图,允许您使用不同的模板引擎,XML,XSLT或任何其他视图技术(ViewResolver接口的实现) 通过使用默认的ApacheCommons文件上传实现或编写自己的MultipartResolver来解析multipart请求 使用任何LocaleResolver实现解决语言环境,包括Cookie,会话,AcceptHTTP标头或用于确定用户期望的语言环境的任何其他方式

处理HTTP请求

首先,让我们来追踪一个简单的HTTP请求到达controller中的方法,然后返回到浏览器/客户端的处理过程。

DispatcherServlet有一个很长的继承关系。它的继承关系是这样的:

GenericServlet<-HttpServlet<-HttpServletBean<-FreamworkServlet<-DispatcherServlet

GenericServlet

GenericServlet是Servlet规范的一部分,它并不直接关注HTTP。它定义了一个service()方法用来接收传递过来的请求,并产生响应。(这里的请求和响应不是指HTTP请求)

publicabstractvoidservice(ServletRequestreq,ServletResponseres) throwsServletException,IOException;

注意,这里的参数中的ServletRequest,ServletResponse并不是和HTTP协议绑定的,Http有具体协议Servlet。

HttpServlet

顾名思义,HttpServlet类就是规范中定义的基于HTTP的Servlet实现。

更实际的说,HttpServlet是一个具有service()方法实现的抽象类,它通过HTTP方法类型分割请求,大致如下所示:

protectedvoidservice(HttpServletRequestreq,HttpServletResponseresp) throwsServletException,IOException{ Stringmethod=req.getMethod(); if(method.equals(METHOD_GET)){ //... doGet(req,resp); }elseif(method.equals(METHOD_HEAD)){ //... doHead(req,resp); }elseif(method.equals(METHOD_POST)){ doPost(req,resp); //... }

根据请求的不同,get,post方法会分别被不同方法处理。

HttpServletBean

上面我们展示过DispatcherServlet的继承关系,在这个继承链中,HttpServletBean是进入spring的第一个层次。从HttpServletBean开始往下的几个servlet都是spring中的类。HttpServletBean就是一个servlet,它继承自HttpServlet,就像是我们在使用servlet+jsp开发时候定义的servlet一样。

根据servlet的生命周期我们知道,servlet会被容器初始化,初始化时候,其init()方法会被调用。在springmvc框架中HttpServletBean使用从web.xml或WebApplicationInitializer收到的servletinit-param值来注入bean的属性。

在请求应用程序的情况下,为这些特定的HTTP请求调用doGet(),doPost()等方法。

FrameworkServlet

FrameworkServlet将Servlet功能与Web应用程序上下文集成,实现ApplicationContextAware接口。但它也能够自行创建Web应用程序上下文。

正如上面所说,FrameworkServlet的超类HttpServletBean将init-param注入为bean属性。所以,如果servlet的contextClassinit-param中提供了context类的名字,那么这个context类的实例将会被创建,用作应用的context。否则,将会使用XmlWebApplicationContext作为默认的。

DispatcherServlet:统一请求处理

有了上面铺垫,我们这里可以开始关键的内容,即DispatcherServlet统一请求处理。在Springmvc的项目中,我们通常会在web.xml配置一个servlet,如下:

<servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>

上面我们提到,DispatcherServlet继承关系,其父类中正儿八经的servlet类是HttpServletBean这个servlet类是应用启动入口。其生命周期的第一阶段init()方法完成初始化工作。

doService()方法设置请求信息

DispatcherServlet初始化之后,便可以工作了。当请求到达之时,会调用其doService()方法。doService()方法的代码如下:

@Override protectedvoiddoService(HttpServletRequestrequest,HttpServletResponseresponse) throwsException{ //删除一下‘非核心代码’方便查看 //Makeframeworkobjectsavailabletohandlersandviewobjects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE,this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE,this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE,getThemeSource()); try{ doDispatch(request,response); } finally{ if(WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()){ return; } //Restoretheoriginalattributesnapshot,incaseofaninclude. if(attributesSnapshot!=null){ restoreAttributesAfterInclude(request,attributesSnapshot); } } }

可以看到,doService()方法先设置一些request信息,这些信息在后续的处理过程中会用到。设置完后,它调用doDispatch()方法。

doDispatch()方法分发请求

doService()方法最终调用了doDispatch(),看名知意,这个方法是做分发工作的。其代码如下:

protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse) throwsException{ //删除一些代码方便阅读 HandlerExecutionChainmappedHandler=null; try{ ModelAndViewmv=null; ExceptiondispatchException=null; try{ //删除一些代码方便阅读 mappedHandler=getHandler(processedRequest,false); HandlerAdapterha=getHandlerAdapter(mappedHandler.getHandler()); try{ //Actuallyinvokethehandler. mv=ha.handle(processedRequest,response,mappedHandler.getHandler()); } finally{ if(asyncManager.isConcurrentHandlingStarted()){ return; } } applyDefaultViewName(request,mv); mappedHandler.applyPostHandle(processedRequest,response,mv); } catch(Exceptionex){ dispatchException=ex;//这里捕获了异常TypeMismatchException } processDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException); } catch(Exceptionex){ } finally{ //删除一些代码 } }

这个方法主要作用就是找到合适的handler来处理请求。handler通常是一个某个类型的对象,并且不限定特定的接口。因此spring需要找到这个handler的适配器。这个Handler通常是一个HandlerMethod实例,

为了找到与请求匹配handler,spring需要从已注册的HandlerMapping接口实现类里边去找。这个查找过程就是在上面的getHandler()方法完成得到的是一个HandlerExecutionChain。这里体现了责任链模式。

这个getHandler()会遍历一个HandlerMapping的map。由于我们一般都使用注解形式:@Controller,@RequestMapping注解。因此这里找到HandlerMapping实现就是RequestMappingHandlerMapping

HandlerAdapterha=getHandlerAdapter(mappedHandler.getHandler());

getHandlerAdapter()方法找到最终的handler适配器,找到的适配器就是RequestMappingHandlerAdapter,(因为我们使用的是@RequestMapping注解形式)。

本例中,我们定义了AppController的hello()方法,并用@Controller*,*@RequestMapping对其分别进行注解,因此这里得到的适配器HandlerAdapter所适配HandlerMethod就是AppController的hello()方法的。

HandlerAdapter处理请求

上面通过确定了HandlerAdapter之后,就要执行handle()方法了,即上面代码中,try语句块里边的ha.handle()。handle()方法定义为:

ModelAndViewhandle(HttpServletRequestrequest, HttpServletResponseresponse, Objecthandler)throwsException;

这个方法有两种处理方式:

自己将数据写到response,然后returnnull 返回一个ModelAndView对象,让DispatcherServlet自己来处理后面渲染工作。

HandlerAdapter有多重类型,例如

SimpleControllerHandlerAdapter处理springmvc的controller实例(注意,不要把这里的controller实例和@Controller注解POJO混淆,这里controller指的是org.springframework.web.servlet.mvc.Controller),并返回ModelAndView,代码如下:

publicModelAndViewhandle(HttpServletRequestrequest, HttpServletResponseresponse,Objecthandler)throwsException{ return((Controller)handler).handleRequest(request,response); }

SimpleServletHandlerAdapter适配的是Servlet作为requesthandler的情况,Servlet是不知道MovelAndView的,所以,它的方法并不负责渲染页面,因此没有返回ModelAndView,只是返回null:

publicModelAndViewhandle(HttpServletRequestrequest, HttpServletResponseresponse,Objecthandler)throwsException{ ((Servlet)handler).service(request,response); returnnull; }

RequestMappingHandlerAdapter就是我们上面提到的,用来处理@Controller和@RequestMapping注解的handler。

渲染视图

handle()方法调用之后,DispatcherServlet可以得到一个ModelAndView,当然也可能是null。对于ModelAndView不为null的时候,DispatcherServlet将会调用render()方法。ModelAndView中可能已经包含了一个view或者只是一个view的名字。如果controller方法指定的是一个字符串形式的视图名字,那么就需要进行试图查找:

for(ViewResolverviewResolver:this.viewResolvers){ Viewview=viewResolver.resolveViewName(viewName,locale); if(view!=null){ returnview; } }

render()方法完成之后,最终的HTML页面会被发送至浏览器端。

当然,springmvc不仅能渲染出页面,也可以返回JSON形式或者XML形式。这种情况controller方法一般都是由*@RequestBody*标注的。这种情况就需要HttpMessageConverter,例如渲染JSON的时候可以使用Jackson包,我们要返回的对象将由,MappingJackson2HttpMessageConverter来转换。

到此,我们就大概说完了springmvc的整个流程。所以,springmvc其实就是一个大的Servlet,接收请求,分发执行请求,我们的每一个controller中的方法都是一个handler,然后最终渲染视图。

spring源码分析400异常处理流程及解决方法

spring如何启动的?这里结合spring源码描述了启动过程

本文内容总结:介绍,从一个项目开始,Servlet是JavaWeb应用的基石,DispatcherServlet是SpringMVC的核心,处理HTTP请求,GenericServlet,HttpServlet,HttpServletBean,FrameworkServlet,DispatcherServlet:统一请求处理,doService()方法设置请求信息,doDispatch()方法分发请求,HandlerAdapter处理请求,渲染视图,

原文链接:https://www.cnblogs.com/demingblog/p/spring.html