springMVC学习一 工作机制
SpringMVC概述
Spring Web MVC 是最初建立在 Servlet API 之上的 Web 框架,从一开始就包含在 Spring Framework 中。 正式名称“Spring Web MVC”来自其源模块(spring-webmvc)的名称,但一般被称之为“Spring MVC”。与 Spring Web MVC 并行,Spring Framework 5.0 引入了一个响应式的Web 框架,其名称“Spring WebFlux”也基于其源模块 (spring-webflux)。
Spring与其他web框架整合思路
spring 框架可以和其他任何MVC框架进行整合,并不是必须和Spring web MVC框架进行整合。
整合的方法就是通过spring框架的ContextLoaderListener来启动spring的根应用程序上下文(root application context),在ContextLoaderListener中的方法initWebApplicationContext中,会把创建的spring根上下文设置成ServletContext的属性。属性名为"org.springframework.web.context.WebApplicationContext.ROOT"。
如果其他MVC框架要想获取spring的根应用上下文,可以通过WebApplicationContextUtils的getWebApplicationContext方法
Spring 的 Web MVC 框架与许多其他 Web MVC 框架一样,是请求驱动的,围绕一个中央 Servlet 设计,该 Servlet 将请求分派给控制器并提供其他功能以促进 Web 应用程序的开发。 然而,Spring 的 DispatcherServlet 不仅仅如此。 它与 Spring IoC 容器完全集成,因此允许您使用 Spring 具有的所有其他功能。
DispatcherServlet实际上 是一个 Servlet(它继承自 HttpServlet 基类),因此在 Web 应用程序的 web.xml 中声明。 您需要通过在同一 web.xml 文件中使用 URL 映射来指定希望 DispatcherServlet 处理的请求。 这是标准的 Java EE Servlet 配置; 以下示例显示了这样的 DispatcherServlet 声明和映射:
<web-app>
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>/example/*</url-pattern>
</servlet-mapping>
</web-app>
Spring与SpringMVC整合的父子容器
默认情况下,在初始化 DispatcherServlet 时,Spring MVC 会在Web 应用程序的 WEB-INF 目录中查找名为 [servlet-name]-servlet.xml 的文件,在[servlet-name]-servlet.xml中定义的bean将会被创建。也可以在配置DispatcherServlet时的初始化参数中配置 应用上下文配置文件的位置。
配置代码如下:
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!--
servlet默认是懒加载,也就是当servlet第一次被访问时,才会加载被访问的servlet,
如果配置了<load-on-startup>,就是在tomcat容器启动时就加载当前servlet
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--
/ 匹配除jsp以外的所有的请求路径
/* 匹配所有请求路径
*.do 匹配所有以.do结尾的所有请求
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
上述配置种,如果没有在<init-param>种指定配置文件位置,则采用默认的配置文件/WEB-INF/dispatcherServlet-servlet.xml
配置注意事项
配置DipatcherServlet时有几个注意的点:
- "/" 代表处理.jsp以外的所有请求都会被DispatcherServlet拦截
- “/*” 代表所有的请求路径都会被拦截
- *.do 匹配所有以.do为结尾的所有请求
因此DispatcherServlet本身没有处理html、css、js、图片等静态资源的能力,所以需要放行静态资源的请求或者交给有能力处理静态资源的组件。
第一种:SpringMVC自己处理静态资源,但是需要指明那些请求是请求静态资源
采取<mvc:resources >
<mvc:annotation-driven />
<mvc:resources location="/img/" mapping="/img/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/css/" mapping="/css/**"/>
location元素表示webapp目录下的static包下的所有文件;
mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b;
该配置的作用是:DispatcherServlet不会拦截以/static开头的所有请求路径,并当作静态资源交由Servlet处理。
第二种:SpringMVC自己不处理静态资源,由tomcat种的默认的Servlet来处理
采用<mvc:default-servlet-handler />
<mvc:default-servlet-handler />
在springMVC-servlet.xml中配置<mvc:default-servlet-handler />后,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。
一般Web应用服务器默认的Servlet名称是"default",因此DefaultServletHttpRequestHandler可以找到它。如果你所有的Web应用服务器的默认Servlet名称不是"default",则需要通过default-servlet-name属性显示指定:
<mvc:default-servlet-handler default-servlet-name="所使用的Web服务器默认使用的Servlet名称" />
HandlerMapping
本质是一个接口,在HandlerMapping的实现类中,有一个属性urlMap,是一个LinkedHashMap,在这个urlMap中 key就是程序员自己编写的controller(也就是处理器)的逻辑路径,例如我们用注解@RequestMapping("/test"),设置的"test",它就是urlMap中的一个key。那么value就是程序员编写的这个Controller。前端控制器(DispatcherServlet)解析出来请求路径后,就去这个处理器映射器中根据请求路径获取要执行的那个controller。
参考别处:HandlerMapping 将会把请求映射为HandlerExecutionChain 对象(包含一个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
下面是一个简单最早的一种配置,现在都使用注解进行controller进行配置
<!-- 注册 处理器,也就是controller --> <bean id="handler" class="com.caopeng.controller.DemoController"></bean> <!-- 注册HandlerMapping 处理器映射器 会根据url请求解析出应该调用哪一个 处理器,但是调用并不是处理器映射器调用 --> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="urlMap"> <map> <!-- key解析出来的请求的逻辑名 value-ref代表要调用的控制器 --> <entry key="demo" value-ref="handler" ></entry> </map> </property> </bean>
处理器的编写代码:早期的写法要实现Controller接口,现在都是用注解进行配置
public class DemoController implements Controller{ @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("执行了springmvc的控制器"); //具备视图和传值的功能 ModelAndView modelAndView =new ModelAndView("main"); return modelAndView; } }
HandlerAdapter
根据处理器映射器 返回的处理器, 把处理器(controller)进行调用
配置如下:
<!-- 注册 处理器适配器,处理器适配器会调用 处理器 处理器适配器可以不用配置,会走默认值 --> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
参考别处:HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView 对象(包含模型数据、逻辑视图名);
ViewResovler
视图解析器.解析结果,准备跳转到具体的物理视图
参考别处:ModelAndView的逻辑视图名——> ViewResolver, ViewResolver 将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
配置如下:
<!-- 在spring容器中 ,注册视图解析器 同样视图解析器也可以不用配置,也会走默认值 InternalResourceViewResolver的父类中有两个属性:prefix,suffix 代表前缀和后缀 ModelAndView modelAndView =new ModelAndView("main"); 前缀和后缀会对ModelAndView中的参数进行拼接,拼接成"/main.jsp", 一般请情况下,我们会使用默认的视图解析器,但是有时候我们会在WebContent下建立一个page目录用来存放 jsp文件,此时在写ModelAndView modelAndView =new ModelAndView("main.jsp");是,需要前面加上 page/main.jsp,为了省略page/一般会把prefix配置成page/ 在返回ModelAndView时,默认会走请求转发,在InternalResourceViewResolver父类UrlBasedViewResolver 中有 public static final String REDIRECT_URL_PREFIX = "redirect:"; public static final String FORWARD_URL_PREFIX = "forward:"; 可以通过配置让其走重定向 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean>
SpringMVC请求处理过程
核心架构的具体流程步骤如下:
首先用户发送请求——>DispatcherServlet:前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;- DispatcherServlet——>HandlerMapping: HandlerMapping 将会把请求映射为HandlerExecutionChain 对象(包含一个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
- DispatcherServlet——>HandlerAdapter:HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
- HandlerAdapter——>处理器功能处理方法的调用:HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView 对象(包含模型数据、逻辑视图名);
- ModelAndView的逻辑视图名——> ViewResolver: ViewResolver 将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
- View——>渲染:View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
- 返回控制权给DispatcherServlet:由DispatcherServlet返回响应给用户,到此一个流程结束。
下边两个组件通常情况下需要开发:
Handler:处理器,即后端控制器用controller表示。
View:视图,即展示给用户的界面,视图中通常需要标签语言展示模型数据。
SpringMVC原理图:
看到这个图大家可能会有很多的疑惑,现在我们来看一下这个图的步骤:(可以对比MVC的原理图进行理解)
第一步:用户发起请求到前端控制器(DispatcherServlet)
第二步:前端控制器请求处理器映射器(HandlerMappering)去查找处理器(Handle):通过xml配置或者注解进行查找
第三步:找到以后处理器映射器(HandlerMappering)像前端控制器返回执行链(HandlerExecutionChain)
第四步:前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)
第五步:处理器适配器去执行Handler
第六步:Handler执行完给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView
第八步:前端控制器请求视图解析器(ViewResolver)去进行视图解析
第九步:视图解析器像前端控制器返回View
第十步:前端控制器对视图进行渲染
第十一步:前端控制器向用户响应结果
参考内容:
https://www.cnblogs.com/fangqi/archive/2012/10/28/2743108.html