springmvc-介绍,重要组件以及执行流程
一、Web、MVC、WebMVC
- Web:Web完成一次请求的过程
- Web浏览器发起请求
- Web服务器接受请求并处理,产生响应
- Web服务器完成处理后,返回内容给Web客户端,Web客户端接收响应的内容,自行处理并显示出来
- 在Web中,都是Web客户端发起的请求,Web服务器接收处理请求并产生响应。
- 一般Web服务器不能主动通知Web客户端更新内容。有一些技术可以从服务器端通知客户端进行更新内容:Comet服务器推技术,还有HTML5中的WebSocket技术
- MVC设计模式(model view controller):是一种设计架构的模式,本生不引入新的功能,只是帮助我们进行功能的细致划分,优化代码结构。
- Model:数据模型。提供要展示的数据,包含数据的封装和操作数据的行为。现在一般将Value Object数据和行为分开。数据有实体类存储或者Java Bean来提供,行为有service层来处理。
- View:负责Model封装实体类数据的展示。
- Controller:接受到用户请求,委托给模型进行处理,处理完毕后把返回的模型数据交给视图。控制器起到一个调度作用。
- 在标准的MVC中,模型能够主动推送数据给视图进行跟新(采用观察者设计模式,在模型上注册视图,在模型更新时候自动跟新视图)。
- 三层架构和WebMVC的区别:
- 三层架构是对整个Web前后端项目进行功能的划分
- MVC是在三层架构中Web层的细致化分,将Web层分为model(数据封装成的模型对象),view(将模型渲染到视图上展示),controller(和Service层进行数据交互,并且负责调度模型和视图)
- WebMVC:Web中MVC里面的模型-视图-控制器的概念和标准MVC概念一样。
- 在标准MVC中,服务器是可以主动推送数据给库和端的;
- 在WebMVC中,服务器是无法实现主动推送数据给客户端的,一定要通过请求和响应,浏览器通过刷新发送请求,服务器再通过响应推送数据给客户端
- Servlet作为Controller,Jsp作为View,Service层作为Model的行为,Java Bean作为Model对象
二、Spring MVC的工作原理
SpringMVC的重要组件
-
SpringWebMVC简称为Spring MVC:SpringMVC是Spring框架提供给我们简化Web项目开发的一个模块,通过SpringMVC很好的将数据、业务和显示进行分离。
-
SpringMVC和其他WebMVC框架一样,都是处理请求和响应。
-
SpringMVC中核心类与接口:
-
DispacherServlet:前端控制器。用来过滤客户端请求
- DispatcherServlet:前置(前端)控制器,可以看出间DispatcherServlet通过Spring的HttpServletBean继承了HttpServlet,所以DispatcherServlet相当于一个Servlet
-
HandlerMapping:处理器/映射器。将请求url按照一定的规则转发给对应的Handler适配器对象。
-
HandlerMapping接口:关联请求url和映射路径
-
HandlerMapping接口的实现类:
-
SimpleUrlhandlerMapping:通过Spring配置文件设置映射器,关联控制器在容器中拦截路径与WebContent中Web.xml的<servlet-mapping)>的
-
DefaultAnnotationHandlerMapping:通过注解,将访问的URl映射到Controller上
-
-
-
HandlerAdapter:适配器。处理适配器要处理的请求Handler对象,指定Controller控制器处理请求和响应
-
HandlerAdapter接口:处理器/适配器,处理请求访问路径选择合适的控制器
-
AnnotationMethodHandlerAdapter类:采用注解方式
-
-
Controller接口:控制器,用来处理请求和响应
-
AbstractController是Controller接口的实现抽象类,对会话实现加锁(同步,避免多个请求同时访问控制器的问题)synchronizeOnSession;指定请求报文请求方法以及是否必须携带Session会话。
//AbstractController抽象类的源码 //多个请求访问Controller,AbstractController保证session多线程访问安全 if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return handleRequestInternal(request, response); } } }
-
-
HandlerInterceptor接口:拦截器,需要我们自己实现该接口
-
ViewResolver接口:视图解析器。
-
UrlBasedViewResovler类:通过spring配置文件,将控制器controller返回的逻辑视图交给ViewResolver进行处理,一般将数据通过EL标签渲染到Jsp页面
-
InternalResourseViewResolver类,支持Jstl标签库的使用
-
-
View接口:视图。通过标签渲染到JSP页面上展示数据。
-
JstlView类
-
-
LocalResolver接口
-
HandlerExceptionResolver接口 --异常处理
-
SimpleMappingExceptionResolver实现类
-
-
-
DispatcherServlet:前置控制器。配置在WebContext的Web.xml文件中。拦截匹配的请求url,Servlet拦截匹配的规则需要自己定义,把拦截请求,再根据我们自己定义的Controller来处理
- 可以在Web.xml文件中配置多个DispatcherServlet
- 需要读取一个配置文件,如果不写,则会默认读取一个固定位置,固定名字的配置文件/WEB-INF/[servletName]-servlet.xml, 生成配置文件中的bean
web.xml 第一个例子:查找默认的Spring配置文件 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <!-- 配置DispatcherServlet前端控制器:相当于之前的Servlet配置 --> <!-- 需要读取一个配置文件,如果不写,则会默认读取一个固定位置,固定名字的配置文件 --> <!-- /WEB-INF/[servletName]-servlet.xml --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-web-mvc.xml</param-value> </init-param> <!-- 启动顺序:让这个Servlet随容器启动而创建 --> <load-on-startup>1</load-on-startup> </servlet> <!-- 自定义拦截规则 --> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <!-- *.do *.html传统的方式。不会导致静态资源被拦截 --> <url-pattern>*.action</url-pattern> <url-pattern>/</url-pattern> </servlet-mapping> </web-app> 第二个例子:Servlet参数配置指定的Spring配置文件,多个Spring配置文件,<value>,</value>中用逗号分隔 <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-web-mvc.xml</param-value> <!--或者采用--> <!--<param-value>/WEB-INF/classes/springMVC.xml</param-value>--> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <!-- "/"拦截所有请求,而不是"/*" --> <url-pattern>/</url-pattern> </servlet-mapping>
-
父子上下文:WebApplicationContext,使用监听器会创建WebApplicationContext
-
Spring会创建一个WebApplicationContext上下文,称为父上下文(父容器) ,保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。
- 可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext);
-
DispatcherServlet是一个Servlet,可以同时配置多个Servlet,每个DispatcherServlet都有自己的上下文对象(WebApplicationContext),称为子上下文(子容器)
- 子上下文可以访问父上下文中的内容,但父上下文不能访问子上下文中的内容。 它也保存在 ServletContext中,key是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。当一个Request对象产生时,会把这个子上下文对象(WebApplicationContext)保存在Request对象中,key是DispatcherServlet.class.getName() + “.CONTEXT”。
- 可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request);
-
说明 :Spring 并没有限制我们,必须使用父子上下文。我们可以自己决定如何使用。
-
一:
- 父上下文容器中保存数据源、服务层、DAO层、事务的Bean
- 子上下文容器中保存Mvc相关的Action的Bean.
- 事务控制在服务层。
- 由于父上下文容器不能访问子上下文容器中内容,事务的Bean在父上下文容器中,无法访问子上下文容器中内容,就无法对子上下文容器中Action进行AOP(事务)。
- 当然,做为“传统型”方案,也没有必要这要做。
-
二:
-
Java世界的“面向接口编程”的思想是正确的,但在增删改查为主业务的系统里,Dao层接口,Dao层实现类,Service层接口,Service层实现类,Action父类,Action。再加上众多的O(vo\po\bo)和jsp页面。写一个小功能 7、8个类就写出来了。
-
开发者说我就是想接点私活儿,和PHP,ASP抢抢饭碗,但我又是Java程序员。最好的结果是大项目能做好,小项目能做快。
-
所以“激进型”方案就出现了-----没有接口、没有Service层、还可以没有众多的O(vo\po\bo)。那没有Service层事务控制在哪一层?只好上升的Action层。
-
由于有了父子上下文,你将无法实现这一目标。解决方案是只使用子上下文容器,不要父上下文容器 。所以数据源、服务层、DAO层、事务的Bean、Action的Bean都放在子上下文容器中。就可以实现了,事务(注解事务)就正常工作了。这样才够激进。
-
-
总结:不使用listener监听器来加载spring的配置文件,只使用DispatcherServlet来加载spring的配置,不要父子上下文,只使用一个DispatcherServlet,事情就简单了。
-
-
SpringMVC框架在项目中的搭建:
-
完整的web.xml配置文件
- DispatcherServlet的配置
- 静态资源采用Tomcat中web.xml配置的默认的defaultServlet处理静态资源文件(解决DispatcherServlet拦截“/”所有的资源问题)
- 过滤器filter(设置请求和响应编码)
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <!-- 配置DispatcherServlet前端控制器:相当于之前的Servlet配置 --> <!-- 需要读取一个配置文件,如果不写,则会默认读取一个固定位置,固定名字的配置文件 --> <!-- /WEB-INF/[servletName]-servlet.xml --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--配置Servlet参数,指定Spring配置文件,解析spring配置文件,生成bean--> <!-- 方法一:默认去WEB-INF下查找 --> <init-param> <param-name>contextConfigLocation</param-name> <!-- 方法二:classpath指定相对路径,相对于src --> <param-value>classpath:spring-web-mvc.xml</param-value> <!-- 方法三:指定WEB-INF下的Spring配置文件 --> <!-- 二者选其一 --> <param-value>/WEB-INF/SpringMVC-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <!-- *.action防止拦截静态资源jsp,html,css,images --> <!-- <url-pattern>*.action</url-pattern> --> <!-- "/"拦截所有请求,而不是"/*" --> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 解决静态资源无法访问问题 --> <!-- 第一种:使用利用Tomcat的web.xml配置文件中defaultServlet来处理静态文件 --> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> <url-pattern>*.js</url-pattern> <url-pattern>*.png</url-pattern> </servlet-mapping> <!-- 过滤器 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
-
Spring配置文件xml:
- 注入映射器(默认映射器),适配器(默认适配器),视图解析器(默认视图解析器,但需要处理逻辑视图名,添加前后缀),控制器(指定自定义控制器,id后者name映射)对象
- 对于Spring配置文件中的处理器适配器,处理器映射器,都可以省去不写,springMVC框架中会有默认配置的,视图解析器也可以不配置,因为在org.springframework.web.servlet.DispatcherServlet这个类的同包下,有一个DispatcherServlet.properties文件,里面就是SpringMVC默认的配置,是当用户的Spring配置文件中没有指定配置时使用的默认策略
- 解决静态资源拦截问题
- 第二种:通过Spring的< mvc:resources>标签
- 第三种: 使用< mvc:default-servlet-handler/>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 解决静态资源无法访问问题 --> <!-- 第二种:通过Spring的<mvc:resources>标签,例如: --> <mvc:resources mapping="/static/ima/**" location="/static/images/" /> <mvc:resources mapping="/static/js/**" location="/static/js/" /> <mvc:resources mapping="/static/css/**" location="/static/css/" /> <!-- 解决方式三: 使用<mvc:default-servlet-handler/>标签在spring配置文件中加入此标签配置即可 --> <mvc:default-servlet-handler/> <!-- 配置HandlerMapping映射器,有默认值 --> <!-- 将url-pattern映射到对应的Controller控制器 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" /> <!-- 配置HandlerAdapter适配器 --> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" /> <!-- 配置ViewResolver视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 支持Jstl标签使用 --> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <!-- 添加逻辑视图名的前缀 --> <property name="prefix" value="/WEB-INF/jsp/" /> <!-- 添加逻辑视图名的后缀 --> <property name="suffix" value=".jsp" /> </bean> <!-- 配置controller控制器 --> <bean name="/hello" class="com.briup.controller.HelloController" /> </beans> 指定src下的Spring配置文件,采用默认的映射器和适配器 <!-- 解决静态资源无法访问问题 --> <!-- 解决方式三: 使用<mvc:default-servlet-handler/>标签在spring配置文件中加入此标签配置即可 --> <mvc:default-servlet-handler /> <!-- 配置ViewResolver视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <!-- 添加逻辑视图名的前缀 --> <property name="prefix" value="/WEB-INF/jsp/" /> <!-- 添加逻辑视图名的后缀 --> <property name="suffix" value=".jsp" /> </bean> <!-- 配置controller控制器 --> <bean name="/hello.action" class="com.briup.controller.HelloController" /> <bean name="/test" class="com.briup.controller.TestController"> <!-- 多个客户端访问session会出现线程安全问题,对session进行加锁 --> <property name="synchronizeOnSession" value="true"></property> <!-- 设置可以接受的请求方法post,get --> <property name="supportedMethods" value="POST,GET"></property> <!-- requireSession,设置请求是否一定要携带session,没有session会抛出异常 --> <property name="requireSession" value="false"></property> </bean>
- 注入映射器(默认映射器),适配器(默认适配器),视图解析器(默认视图解析器,但需要处理逻辑视图名,添加前后缀),控制器(指定自定义控制器,id后者name映射)对象
-
自定义Controller控制器
//处理器,控制器 //接收前端的请求,处理请求,返回model和view public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception { //模型和视图 ModelAndView mv = new ModelAndView(); //往模型对象中存放值 mv.addObject("name", "tom"); //指定跳转的逻辑视图名 mv.setViewName("hello"); return mv; } } //研究Controller的实现类AbstractController,实现线程同步,请求访问方法 public class TestController extends AbstractController { @Override protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { //ModelAndView mv = new ModelAndView("hello"); //mv.addObject("name", "zs"); PrintWriter writer = response.getWriter(); writer.write("hello world!"); writer.flush(); return null; } }
-
展示页面jsp:放在WEB-INF下的页面,只能通过内部跳转的方式访问到,因为客户端访问不到WEB-INF目录,而且服务器端可以访问到WEB-INF目录
- 需要引入jstl相关jar包
- 页面中的路径问题 ,设置basePath
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/"; %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>hello.jsp</title> <base href="<%=basePath %>"> <link href="static/css/hello.css" rel="stylesheet" type="text/css"/> </head> <body> <h1>hello,${name }</h1> </body> </html>
-
-
整个访问流程:
- 首先用户发送请求,前置控制器DispatcherServlet收到请求后自己不进行处理,而是委托给其他解析器进行处理,前置控制器作为唯一的访问点,进行全局的流程控制。
- DispatcherServlet把请求转交给HandlerMapping,HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象.(后面会学习到拦截器)
- DispatcherServlet再把请求转交给HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器(适配器模式).简单点说就是让我们知道接下来应该调用Controller处理器里面的什么方法
- HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名)
- ModelAndView的逻辑视图名交给ViewResolver解析器, ViewResolver解析器把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术
- View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术
ionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象.(后面会学习到拦截器) - DispatcherServlet再把请求转交给HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器(适配器模式).简单点说就是让我们知道接下来应该调用Controller处理器里面的什么方法
- HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名)
- ModelAndView的逻辑视图名交给ViewResolver解析器, ViewResolver解析器把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术
- View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术
- 最后返回到DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。
上一篇: 程序员困境:底层编码能力正逐步丧失
下一篇: Redis安装与基本介绍