一.SpringMVC是什么
SpringMVC是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合。
SpringMVC是一个基于mvc的web框架。
SpringMVC表现层:方便前后端数据的传输。
SpringMVC 拥有控制器,作用跟Struts类似,接收外部请求,解析参数传给服务层
MVC是指,C控制层,M模块层,V显示层这样的设计理念,而SSM框架里面SPRING MVC本身就是MVC框架。
1、特性
(1)简单、容易上手;
(2)性能优异:jsp+servlet>Struts=SpringMVC>Struts2;
(3)灵活、易于扩展;
(4)易于和Spring容器整合;
二.SpringMVC能做什么
1、为什么要使用springMVC?
很多应用程序的问题在于处理业务数据和显示业务数据的视图的对象之间存在紧密耦合。通常,更新业务对象的命令都是从视图本身发起的,使视图对任何业务对象更改都有高度敏感性。
而且,当多个视图依赖于同一个业务对象时是没有灵活性的。
SpringMVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,
将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。
(1)Spring MVC 实现了即用的 MVC 的核心概念。它为控制器和处理程序提供了大量与此模式相关的功能。
并且当向 MVC 添加反转控制(Inversion of Control,IoC)时,它使应用程序高度解耦,提供了通过简单的配置更改即可动态更改组件的灵活性。
(2)Spring 的 Web MVC 模块是围绕 DispatcherServlet 而设计的。DispatcherServlet 给处理程序分派请求,执行视图解析,并且处理语言环境和主题解析,此外还为上传文件提供支持。
(3)DispatcherServlet 通过使用处理程序映射来决定哪一个处理程序应当处理传入的请求。处理程序映射只是用于标识使用哪一个处理程序来处理特定 URL 模式的映射。
处理程序是只有一种方法 ModelAndView handleRequest(request,response) 的控制器接口的实现。
Spring 还有一些可用的高级处理程序实现;其中一个重要的高级处理程序实现是 SimpleFormController,它提供了将命令对象绑定到表单、对其执行验证等功能。
2、SpringMVC优势
(1)清晰的角色划分:前端控制器(DispatcherServlet)、请求到处理器映射(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)、
处理器或页面控制器(Controller)、验证器( Validator)、命令对象(Command 请求参数绑定到的对象就叫命令对象)、表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)。
(2)分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要;
(3)由于命令对象就是一个POJO,无需继承框架特定API,可以使用命令对象直接作为业务对象;
(4)和Spring 其他框架无缝集成,是其它Web框架所不具备的;
(5)可适配,通过HandlerAdapter可以支持任意的类作为处理器;
(6)可定制性,HandlerMapping、ViewResolver等能够非常简单的定制;
(7)功能强大的数据验证、格式化、绑定机制;
(8)利用Spring提供的Mock对象能够非常简单的进行Web层单元测试;
(9)本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换。
(10)强大的JSP标签库,使JSP编写更容易。
(11)还有比如RESTful风格的支持、简单的文件上传、约定大于配置的契约式编程支持、基于注解的零配置支持等等。
三.SpringMVC的原理
1、Spring MVC 的请求流程
每当用户在 Web 浏览器中点击链接或者提交表单的时候,请求就开始工作了,像是邮递员一样,从离开浏览器开始到获取响应返回,
它会经历很多站点,在每一个站点都会留下一些信息同时也会带上其他信息。
下图为 Spring MVC 的请求流程:
(1)DispatcherServlet
从请求离开浏览器以后,第一站到达的就是 DispatcherServlet,看名字这是一个 Servlet,
Servlet 可以拦截并处理 HTTP 请求,DispatcherServlet 会拦截所有的请求,并且将这些请求发送给 Spring MVC 控制器。
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <!-- 拦截所有的请求 --> <url-pattern>/</url-pattern> </servlet-mapping>
DispatcherServlet 的任务就是拦截请求发送给 Spring MVC 控制器。
(2)处理器映射(HandlerMapping)
问题: 典型的应用程序中可能会有多个控制器,这些请求到底应该发给哪一个控制器呢?
所以 DispatcherServlet 会查询一个或多个处理器映射来确定请求的下一站在哪里,处理器映射会根据请求所携带的 URL 信息来进行决策。
<bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <!-- /hello 路径的请求交给 id 为 helloController 的控制器处理--> <prop key="/hello">helloController</prop> </props> </property> </bean> <bean id="helloController" class="controller.HelloController"></bean>
(3)控制器
一旦选择了合适的控制器, DispatcherServlet 会将请求发送给选中的控制器,到了控制器,请求会卸下其负载(用户提交的请求)等待控制器处理完这些信息:
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { // 处理逻辑 .... }
(4)返回 DispatcherServlet
当控制器在完成逻辑处理后,通常会产生一些信息,这些信息就是需要返回给用户并在浏览器上显示的信息,它们被称为模型(Model)。
仅仅返回原始的信息时不够的——这些信息需要以用户友好的方式进行格式化,一般会是 HTML,所以,信息需要发送给一个视图(view),通常会是 JSP。
控制器所做的最后一件事就是将模型数据打包,并且表示出用于渲染输出的视图名(逻辑视图名)。它接下来会将请求连同模型和视图名发送回 DispatcherServlet。
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { // 处理逻辑 .... // 返回给 DispatcherServlet return mav; }
(5)视图解析器
这样以来,控制器就不会和特定的视图相耦合,传递给 DispatcherServlet 的视图名并不直接表示某个特定的 JSP。
(实际上,它甚至不能确定视图就是 JSP)相反,它传递的仅仅是一个逻辑名称,这个名称将会用来查找产生结果的真正视图。
DispatcherServlet 将会使用视图解析器(view resolver)来将逻辑视图名匹配为一个特定的视图实现,它可能是也可能不是 JSP。
(6)视图
既然 DispatcherServlet 已经知道由哪个视图渲染结果了,那请求的任务基本上也就完成了。
它的最后一站是视图的实现,在这里它交付模型数据,请求的任务也就完成了。视图使用模型数据渲染出结果,这个输出结果会通过响应对象传递给客户端。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%>
<h1>${message}</h1>
总结:
具体流程步骤如下:
a、首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
b、DispatcherServlet——>HandlerMapping,处理器映射器HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、
多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
c、DispatcherServlet——>HandlerAdapter,处理器适配器HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
d、HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
e、ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
f、View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
g、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。
2、SpringMVC核心组件
(1)前端控制器DispatcherServlet(不需要开发)
作用接收请求,响应结果,相当于转发器,中央处理器。有了DispatcherServlet减少了其它组件之间的耦合度。
用户发送请求交给DispatcherServlet,DispatcherServlet是整个流程控制的中心,由它调用其他组件处理用户请求,分发到具体的对应Controller,从而获取到需要的业务数据Model,
Model再通过DispatcherServlet传递给View完成页面呈现;DispatcherServlet的存在降低了组件的之间的耦合性。
(2)处理器映射器HandlerMapping(不需要开发)
作用:根据请求的url查找Handler。
HandlerMapping负责根据用户请求找到Handler即处理器,SpringMVC提供了多种不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
(3)HandlerInterceptor:Handler执行前后拦截器
HandlerInterceptor是个接口,里面包含三个方法:preHandle、postHandle、afterCompletion。
分别在Handler执行前、执行中、执行完成后执行的三个方法。
(5)HandlerExecutionChain:HandlerMapping返回给DispatcherServlet的执行链
HandlerMapping返回给DispatcherServlet的不光有Handler,还有HandlerInterceptor。
preHandle——>ControllerMethod——>postHandle——>afterCompletion
(3)处理器适配器HandlerAdapter
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
(4)处理器Handler(需要开发)
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
Handler是继DispatcherServlet前端控制器的后台控制器,在DispatcherServlet控制下对用户请求进行处理,Handler涉及业务需求,所以需要工程师针对用户需求进行开发,最终返回业务数据。
(5)ModelAndView:SpringMVC中对Model的一种表示形式
SpringMVC中有Model、Map,但是SpringMVC都会将其转化为ModelAndView,Model、Map都是ModelAndView的具体表现。
(6)视图解析器View resolver(不需要开发)
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成具体的页面地址,然后对View进行渲染,将处理结果通过页面展示给用户;
SpringMVC提供了很多类型View视图,包括:jstlView、freemarkerView、pdfView、jsp、html等。
(7)视图View(需要开发jsp)
View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)
四.SpringMVC的使用
程序示例如下:
1、xml配置版
(1)创建动态Web项目;
(2)导入spring的jar包,导入common-logging的jar包;
(3)为web项目引入SpringMVC框架,即在web.xml中配置SpringMVC的DispatcherServlet;
<!-- 配置DispatcherServlet --> <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- SpringMVC的配置文件所在的位置和名称 --> <init-param> <param-name>contextConfigLocation</param-name> <!--框架进行转发规则的定义文件--> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <!-- SpringMVC拦截所有请求,再进行转发 --> <url-pattern>/</url-pattern> </servlet-mapping>
在src目录下创建SpringMVC的配置文件springmvc.xml,并加入如下代码:
... <!--配置方式必须写的两个,每个请求对应一个Controller,不用此种方式--> <!-- 标明所有实现了org.springframework.web.servlet.mvc.Controller接口的Bean可以作为Spring Web MVC中的控制器 --> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!-- 表示将请求的URL和Bean名字映射 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!-- 一个具体的映射,表示将hello.action地址映射到了HelloController的方法上 --> <bean name="/hello.action" class="com.controller.HelloController"/> ...
编写接受请求的控制器:
//实现Controller接口,配置方式需要实现Controller类,相当于servlet继承HttpServlet public class HelloController implements Controller{ //重写Controller接口的handleRequest方法 public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception { //新建一个视图对象 ModelAndView mav = new ModelAndView(); //给视图对象里添加键值对形式的存储结构,相当于返回的键值对 mav.addObject("message", "hello springmvc"); //指定视图的jsp文件 mav.setViewName("hello.jsp"); return mav; } }
编写hello.jsp文件
<body> 获取值${message} </body>
2、注解版
在src目录下创建SpringMVC的配置文件springmvc.xml,并加入如下代码:
... <!-- 配置扫描包,告诉SpringMVC被注解的class都在哪个包下 --> <context:component-scan base-package="com.controller"></context:component-scan> <!-- 配置视图解析器,告诉SpringMVC在网站的哪个目录下能找到jsp文件 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property> </bean> ...
编写接受请求的控制器:
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloWorld { /** * 1. 使用RequestMapping注解来映射请求的URL,写在方法上面,一个请求对应一个方法 * 2. 返回值会通过视图解析器解析为实际的物理视图, 会做如下解析 * 通过prefix+returnVal+suffix 这样的方式得到实际的物理视图,然后会转发操作 * "/WEB-INF/views/success.jsp" */ @RequestMapping("/helloworld") public String hello(){ System.out.println("hello world"); return "success"; } }
3、映射规则
二级映射
在类上和方法上同时注解@RequestMapping,相当于地址栏里有两级的地址
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;
@Controller @RequestMapping("one") public class TestController { @RequestMapping("two") public String test(){ return "index"; } } //如上注解后,映射地址为:http://localhost:8080/xx/one/two
Method类型选择接受
@RequestMapping注解括号里,有method属性作为对提价类型的选择接受
@RequestMapping(value="/getName",method={RequestMethod.GET, RequestMethod.POST}) //如果没有指定method,则默认为所有请求类型都接受
参数规则匹配:
在响应请求的时候,对提交的参数规则进行验证,如果不符合设置的规则,则不接受请求;
(1)param1=value1 - 请求中必须包含该参数和指定值;
(2)param2 - 请求中必须包含该参数,值任意;
(3)!param3 - 请求中必须不包含该参数;
@RequestMapping(value="/getName",method=RequestMethod.GET,params={"id=123","name","!age") //上述规则定义了,只能响应get请求,并且请求的参数必须包含id=123,必须包含name,不能包含age //根据上述规则此地址合法:http://localhost:8080/xx?id=123&name=abc
参数绑定的含义:
(1)所谓的参数绑定,就是怎么样获取到前台页面传过来的值,通常是跟据参数名(key)来获取值;
绑定页面传值的key值到映射方法的参数上,如下程序所示
//页面端提交请求的程序 $.post("../hc.v", { name : "shoji", price : "8888" }, function(d) { alert(d); } ) //后台响应上面ajax的post请求的代码 //通过两段代码里“name”和“price”的相同,把“shouji”和“8888”传到hc方法里 //问号传值的方式同样适用 ?name=shoji&price=8888
... @RequestMapping("hc") public String hc(String name,String price){ return "test"; } ...
(2)用标准PO属性来进行绑定
页面端提交请求的js代码同上
//新建一个标准的PO,同时当前类的属性名应该跟前台代码里的KEY对应上 public class PO{ private String name;//和key值一样 private Stirng price;//和key值一样 //省略各自的set get } //后台响应上面ajax的post请求的代码 //通过PO里的“name”和“price”属性名和前台js代码里的key相同,把“shouji”和“8888”传到hc方法里 //问号传值的方式同样适用 ?name=shoji&price=8888
... @RequestMapping("hc") public String hc(PO po){ //po.getName() 取出shoujie //po.getPrice() 取出8888 return "test"; } ...
(3)用注解@RequestParam来绑定
页面端提交请求的js代码同上
//后台响应上面ajax的post请求的代码 //通过注解里的“name”和“price”参数名和前台js代码里的key相同,把“shouji”和“8888”传到hc方法里 //问号传值的方式同样适用 ?name=shoji&price=8888 ... @RequestMapping("hc") public String hc(@RequestParam("name") String p1,@RequestParam("price") String p2){ //p1 取出shoujie //p2 取出8888 return "test"; } ...
(4)通过注解@PathVariable
本注解把要传给后台程序的值,绑定在地址里;
页面端提交请求的地址变为:dianhua/name/shoji/price/8888
//后台响应上面ajax的post请求的代码 //@RequestMapping注解的映射改变,地址里值的部分用{}括起来 //在方法的参数上使用注解@PathVariable //通过PathVariable注解后面的“idvalue”和“pricevalue”和RequestMapping注解里{}部分相同,把“shouji”和“8888”传到hc方法里 @RequestMapping("dianhua/name/{idvalue}/price/{pricevalue}") public String hc(@PathVariable String idvalue,@PathVariable String pricevalue){ //idvalue 取出shoujie //pricevalue 取出8888 return "test"; } ...
4、映射方法的返回值类型
(1)返回ModelAndView
<!-- springmvc配置文件代码片段 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property> </bean>
@RequestMapping("/szw/jintiancailai") public ModelAndView ccc(String namee) { List list = new ArrayList(); list.add("zhiweizhi"); list.add("luchang"); System.out.println("hello name=" + namee); ModelAndView mav = new ModelAndView(); mav.addObject("myList", list); mav.addObject("mySize", "2"); mav.setViewName("first"); //first是一个页面文件的名字 //在springmvc的配置文件指定页面文件所在的路径是/WEB-INF/views/ //在springmvc的配置文件指定页面文件的类型是.jsp //我们ModelAndView所展示的页面会做如下解析 prefix+setViewName+suffix //即 /WEB-INF/views/first.jsp return mav; }
ModelAndView是springmvc框架提供一个包装界面jsp文件的类,常用的方法addObject和setViewName。
addObject有两个参数,类型都是Object类型,第一个参数是key,第二个参数是值;主要用途,把java后的里的数据带到前台jsp页面上。
setViewName有一个参数,类型是String;参数是jsp文件的名字;用途是告诉ModelAndView所展示的页面是哪个jsp文件。
(2)返回值是String
返回值里只有一个字符串
public String ddd() { return "first"; } //这样返回同上面程序注释的过程
redirect和forward在返回值字符串前,并且redirect和forward和字串之间有一个冒号。
public String eee() { return "redirect:../first.jsp"; } public String fff() { return "forward:../first.jsp"; } //这样返回,我们转去的地址不在被springmvc管理,需要转去的地址能直接在地址栏访问
@ResponseBody注解:
当返回值是String,并且方法上方有此注解时,返回的字符串将不在被解析为springmvc的视图(即jsp文件),会被直接以字符串展示在浏览器里。
@RequestMapping("/test") @ResponseBody public String hhh() { return "first"; } //first将被输出到浏览器里
返回时void:
需要人为在方法的参数列表里,加入request和response这两个参数,所有的操作依靠这两个参数来完成,非常类似标准的Servlet。
public void ggg(HttpServletRequest req,HttpServletResponse rep) { //标准servlet里怎么写,这段代码就怎么写 }
SpringMVC与struts2的区别
(1)springmvc基于方法开发的,struts2基于类开发的。springmvc将url和controller里的方法映射。映射成功后springmvc生成一个Handler对象,对象中只包括了一个method。
方法执行结束,形参数据销毁。springmvc的controller开发类似web service开发。
(2)springmvc可以进行单例开发,并且建议使用单例开发,struts2通过类的成员变量接收参数,无法使用单例,只能使用多例。
(3)经过实际测试,struts2速度慢,在于使用struts标签,如果使用struts建议使用jstl。