此篇博文讲解Spring MVC环境的搭建,并实现视图层(View)与控制器(Controller)之间的参数传递。理解MVC的设计模式,Spring MVC的架构以及请求处理流程。Spring MVC 开发环境的搭建,掌握Controller和View之间的映射,掌握参数传递(View-Controller)掌握模型数据处理。
数据访问接口(DAO层)
处理业务逻辑(Service层)
数据实体层(POJO层)
负责前端请求的处理并接受(Servlet 控制层)
前端页面的展示(JSP 表示层)
这种架构模式就是MVC设计模式,它是软件工程中的一种架构模式,强制性地使软件系统的输入、处理和输出分开,把系统分为三个基本部分:模型(Model)、视图(View)、控制器(Controller),如图:
Model1(模式一)(了解即可,一般不会用)
Model2(模式二) 相当于(JavaBean+Servlet+Jsp)
相比于JSP Model1,当业务流程比较复杂的时候,就需要把业务流程控制交给专门的控制器,JSP只专注于视图的渲染展现即可。这种模式就是Model2,即JSP+Servlet+JavaBean,也就是典型的MVC设计模式。如图:
(引用别人的图片)
对于MVC来说,它并不适用于小型的项目,花费大量的时间将MVC应用到项目中通常得不偿失,夸张点说,可能搭建三层架构、构建MVC开发环境的时间,都可以实现整个项目功能了。所以,是否使用MVC模式,应该根据实际场景来决定。
上面介绍的是MVC设计模式,是一种理念,下面介绍MVC框架
Spring MVC是Spring家族中应用于Web应用的一个模块,是Spring提供的一个基于MVC设计模式的Web开发框架,可以将它理解为Servlet。在MVC模式中,Spring MVC作为控制器(Controller)来建立模型与视图的数据交互,是结构最清晰的MVC odel2实现,可以说是一个典型的MVC框架
Spring MVC框架采用松耦合、可插拔的组件结构,具有高度可配置性,比起其他的MVC框架更具有扩展性和灵活性。并且它本身就是Spring家族的一部分,与Spring框架整合更是天衣无缝。
在Spring MVC框架中,Controller(控制器)替换Servlet来担负控制器的职责。Controller接收请求,调用相应的Model进行处理,Model处理完之后将结果返回给Controller,Conreoller再指派相应的View对处理结果进行渲染,最终响应给客户端进行展示。
在前面章节中的JAR添加以下两个Jar包
spring-web-3.2.13.RELEASE.jar
在Web应用程序开发时,使用Spring框架所需的核心类
spring-webmvc-3.2.13.RELEASE.jar
Spring MVC框架相关的所有类,包含框架的Servlet,Web MVC框架,以及对控制器和相关视图的支持。
web.xml中配置Servlet
Spring MVC是基于Servlet的,DispatcherServlet是整个Spring MVC框架的核心,它负责截获请求并将其分派给相应的处理器。那么搭建Spring MVC环境,首先我们要进行DispatcherServlet的配置。同之前配置Servlet一样,因为通过读取源码我们可以发现,DispatcherServlet这个类继承了FrameworkServlet,FrameworkServlet又继承了HttpServletBean,HttpServletBean最终继承了HttpServlet,所以可以说DispatcherServlet本身就是一个Servlet。
<servlet>
<servlet-name>IndexControllerservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc-servlet.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>IndexControllerservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
(1)配置了一个名为mvcDemo的Servlet。
(2)该Servlet是DispatcherServlet类型,前面已经介绍过Sprnig MVC本质就是一个Servlet,它是Spring MVC的入口,
(3)通过
指定容器在启动的时候就创建此Servlet,
(4)然后通过servlet-mapping映射到 "/"
,即所有的URL请求都会被该Servlet截获。
(5)最重要的一点,我们需要设置一个参数contextConfigLocation
参数来指定Spring MVC配置文件的路径(下面创建的XML),此处使用资源路径的方式进行指定(classpath:……)。这个参数也可以声明成上下文参数,保证能读取到就行。
创建Spring MVC 的配置文件(springmvc-servlet.xml)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean name="/welcome" class="com.bdqn.controller.IndexController"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
beans>
配置处理映射器
我们在web.xml中配置DispatcherServlet时,配置了所有请求都由此Servlet来处理,那么DispatcherServlet怎么判断它将哪个请求、分发给哪个Controller(控制器)去处理呢?DispatcherServlet需要咨询一个Bean组件,这个Bean叫做HandlerMapping,它的作用就是将一个URL请求映射到相应的Controller,类似于servlet-mapping中的url-pattern。
Spring MVC提供了多种处理器映射(HandlerMapping)的支持,如:
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
org.springframework.web.servlet.mvc.annotation.DefaultAnnontationHandlerMapping;
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
根据需求选择处理器映射,此处我们采用BeanNameUrlHandlerMapping(默认的)如果没有标明任何处理器,Spring默认使用 BeanNameUrlHandlerMapping 即在Spring容器中查找与请求URL相同名称的Bean。这个映射器不需要配置,根据请求的URL路径映射到控制器Bean的名称。至于其他的处理器映射,在以后相继介绍。
<bean name="/welcome" class="com.bdqn.controller.IndexController"/>
配置视图解析器
处理请求的最后一件必要的事情就是渲染输出,返回给客户端显示,这个任务由视图(View)实现。DispatcherServlet会查找到一个视图解析器,将控制器返回的逻辑视图名称转换成实际视图。Spring也提供了多种视图解析器,如下:
org.springframework.web.servlet.view.InternalResourceViewResolver;
org.springframework.web.servlet.view.ContentNegotiatingViewResolve
此处我们使用InternalResourceViewResolver
定义该视图解析器,通过配置prefix(前缀)和suffix(后缀),最后将视图逻辑名解析为"/WEB-INF/jsp/"
,即定位到"jsp"
文件夹下的".jsp"
文件。
将一个JavaBean变成可以处理前端请求的控制器需要继承org.springframework.web.servlet.mvc.AbstractController
,并实现其handleRequestInternal
方法,代码如下:
package com.bdqn.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
//控制器:IndexController 继承AbstractController实现handleRequestInternal
public class IndexController extends AbstractController{
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest arg0,HttpServletResponse arg1) throws Exception {
System.out.println("学框架就学Spring MVC!");
return new ModelAndView("welcome");
}
}
控制器处理方法的返回值为ModelAndView对象,该对象既包含视图信息,也包含模型数据信息,这样Spring MVC就可以使用视图对模型数据进行渲染。其中“welcome
”就是视图的逻辑名,最后由视图解析器处理就会变成/WEB-INF/jsp/welcome.jsp
视图逻辑名:就一个单纯的名字,没有后缀,就是逻辑上的一个视图名字
(1)ModelAndView,正如其名所示,它代表Spring MVC中呈现视图界面时所使用的Model(模型数据)和View(逻辑视图名)。
(2)由于Java一次只能返回一个对象,所以ModelAndView的作用就是封装这两个对象,以方便一次返回我们所需的Model和View。
(3)当然,返回的模型跟视图都是可选的,有时候模型中没有任何数据,那么只返回视图即可;或者只返回模型数据,让Spring MVC根据请求URL来决定视图。
我们的Controller返回的逻辑视图名被解析之后,会转换成"/WEB-INF/jsp/welcome.jsp"
,所以我们需要在jsp文件夹下存在index.jsp文件。
Spring MVC 的处理流程:
但是如果有多种请求,岂不是要在 springmvc-servlet 中配置多个映射关系?针对这种问题,Spring MVC提供了一键式配置方法:
,通过注解的方式定义Controller,结合实例来理解。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.bdqn.controller">context:component-scan>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
beans>
package com.bdqn.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController2 {
@RequestMapping("/welcome2")
public String ZhuController(){
System.out.println("注解进入");
return "welcome1";
}
}
Spring MVC框架的体系结构
(1)客户端发出HTTP请求,Web应用服务器接收此请求。如匹配DispatcherServlet的请求映射路径,则Web容器将该请求转交给DispatcherServlet处理;
(2)DispatcherServlet拿到请求之后,根据请求的信息(URL、请求参数、HTTP方法等)及HandlerMapping的配置找到处理请求的处理器(Handler);
(3)当DispatcherServlet找到相应的Handler之后,通过HandlerAdapter对Handler进行封装,再以统一的适配器接口调用Handler。HandlerAdapter可以理解为真正使用Handler来干活的人。
(4)在请求信息真正到达调用Handler的处理方法之前的这段时间,Spring MVC还完成了很多工作,它会将请求信息以一定的方式转换并绑定到请求方法的入参,对于入参的对象会进行数据转换、数据格式化以及数据校验等。这些都做完以后,最后才真正调用Handler的处理方法进行相应的业务逻辑处理。
(5)处理器完成业务处理之后,将一个ModelAndView对象返回给DispatcherServlet,其中包含了逻辑视图名和模型数据信息。
(6)DispatcherServlet通过ViewResolver将逻辑视图名解析为真正的视图对象View,可以是JSP、HTML、XML、PDF、JSON等等,Spring MVC均可灵活配置,在以后会介绍。
(7)得到真正的视图对象之后,DispatcherServlet会根据ModelAndView对象中的模型数据对View进行视图渲染。
(8)最终客户端获得响应消息。
Spring MVC框架的特点
(1) 角色划分清晰。Model、View、Controller各司其职,耦合度较低。
(2)灵活的配置功能。Spring的核心是IoC和AOP,统一可以实现在MVC上,把各种类当作Bean组件配置在Spring容器中。
(3)提供了大量的接口和实现类,方便各种场景的开发。
(4)真正做到与View层的实现无关。
(5)结合Spring的依赖注入,更好地应用了面向接口编程的思想。
(6) 可以与Spring天衣无缝的整合
View to Controlle 方式:
value: 指定 URL 请求的实际地址,是 @RequestMapping 的默认值。
@RequestMapping("hello")
public String hello(){
System.out.println("hello world");
return "index";
}
等同于:
@RequestMapping(value="hello")
public String hello(){
System.out.println("hello world");
return "index";
}
method:指定请求的 method 类型,GET、POST、PUT、DELETE 等。
@RequestMapping(value="/postTest",method=RequestMethod.POST)
public String postTest(){
System.out.println("postTest");
return "index";
}
表示只有 POST 请求可以访问该方法,若使用 GET 请求访问,直接报错。
其他几种方式同理,限定了请求的类型,我们常用的是 GET 和 POST、PUT,DELETE 是 rest 风格的表现形式。
params: 指定 request 中必须包含某些参数值,否则无法调用该方法。
@RequestMapping(value="paramsTest",params={"name","id=10"})
public String paramsTest(){
System.out.println("paramsTest");
return "index";
}
URL 请求中必须包含 name 和 id 两个参数,并且 id 的值必须为 10,才能调用 paramsTest 方法。
上述 3 种常见的错误,请求必须同时包含 name 和 id 参数,并且 id=10 才可正常访问业务方法。
@RequestMapping可以在控制器的类定义处指定URL
在控制器的类定义处以及方法定义处都可添加 @RequestMapping,在类定义处添加 @RequestMapping 注解,相当于多了一层访问路径
@Controller
@RequestMapping("/helloHandler")
public class HelloHandler {
@RequestMapping(value="hello")
public String hello(){
System.out.println("hello world");
return "index";
}
}
(2)通过请求参数、请求方法、请求URL进行映射
@RequestMapping(value = "welcome",method = RequestMethod.GET,params = "username")
public String welcome(@RequestParam String username){
System.out.println("welcome--->"+username);
return "welcome2";
}
@RequestMapping的value表示请求的URL,method表示请求方法,此次设置了GET,所以POST请求是进不来的,param表示请求参数,此处我们的参数名必须包含username。 下面分析一下:
首先,value(请求路径)匹配,method(超链接是GET请求)匹配,请求参数(username)也匹配,所以进入了处理方法。
在方法入参处使用@RequestParam注解指定其对应的请求参数。@RequestParam有以下三个参数:
@RequestMapping(value = "/welcome",method = RequestMethod.GET)
public String welcome(@RequestParam(value = "name",required = false) String userName){
//如果方法的入参名与传来的参数名一致,可省略@RequestParam注解
System.out.println(userName);
// model.addAttribute("userName",name);
return "welcome";
}
这种情况下不要求方法的入参名与请求中的参数名一致,保证@RequestParam注解能够将二者关联上即可。但是为了规范起见,建议一致。
了解完从View到Controller的传参,接下来学习Controller到View的传参。
这就需要模型数据的处理了,对于MVC框架来说,模型数据是最重要的一部分,因为控制层是为了产生模型数据(Model),而视图(View)最终也是为了渲染模型数据并进行输出。那么如何将模型数据传递给视图?Spring MVC提供了多种方式,介绍一下比较常用的:
控制器的处理方法的返回值若是ModelAndView,即既包含Model,也包含View。那么拿到该对象之后,Spring MVC就可以使用视图对模型数据进行渲染
ModelAndView对象的常用方法如下:
添加模型数据
设置视图
Spring MVC在调用方法前会创建一个隐含的模型对象,作为模型数据的存储容器,一般称为“隐含模型”。若处理方法的入参为Model类型,Spring MVC会将这个隐含模型的引用传递给这些入参,这样开发者就可以通过一个Model类型的入参,访问到模型中的所有数据,当然也可以做修改、添加等。
不管是ModelAndView还是Model,它们的用法很类似,运用起来也比较灵活。Model对象的addAttribute()方法和ModelView对象的添加模型数据的方法的用法是一样的,即Model对象也是一个Map类型的数据结构。
另外,对于Model对象的addAttribute()方法,key的指定并不是必须的,可以不指定。那么既然它是一个Map结构,如果不指定它会以什么为key呢?这种情况下,会默认使用对象的类型作为key。比如上面的示例中,我们的name是字符串类型的,我们如果不指定key,则“string”就是它的key,访问的时候即是${string}。不过个人不推荐此用法,容易混乱。
除此之外,addAttribute()方法可以添加任何数据类型,JavaBean、List、Map等都可以。
不拿发现,Spring MVC的Model其实就是一个Map数据结构,故我们使用Map为处理方法入参也是可行的。
Spring MVC控制器的处理方法若有Map或者Model类型的入参,就会将请求内的隐含模型对象传递给这些入参,因此可以在方法体内对模型数据进行读写。推荐使用Model。
若希望将入参的数据对象放入数据模型中,就需要在相应入参前使用此注解。在以后的博文讲解,此处知道有这么个东西即可。
此注解可以将模型中的属性存入HttpSession中,以便在一次会话中共享该属性。
请求处理方法执行完成之后,最终返回一个ModelAndView对象。对于那些返回String类型的处理方法,Spring MVC内部也会将它们装配成一个ModelAndView对象,它包含逻辑视图名和数据模型,那么接下来的工作就交给视图解析器(ViewResolver)了。
ViewResolver是Spring MVC处理视图的重要接口,通过它可以将控制器返回的逻辑视图名解析成一个真正的视图对象。Spring MVC默认提供了多种视图解析器,所有的视图解析器都实现了ViewResolver接口。
对于JSP这种常见的视图技术,通常使用InternalResourceViewResolver作为视图解析器。那么它是如何工作的?
InternalResourceViewResolver是最常用的视图解析器,通常用于查找JSP和JSTL等视图。通过源码就可以看出,它是URLBasedViewResolver的子类,会将返回的逻辑视图名都解析成InternalResourceViewResolver对象,该对象会把Controller的处理方法返回的模型属性都放在对应的请求作用域中,然后通过RequestDispatcher在服务器端把请求转发到目标URL。
我们只需要在配置文件中进行如下配置:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/" />
<property name="suffix" value=".jsp" />
bean>
Spring MVC要记得概念比较多哦,所有我们必须要理解,如果不理解死记硬背是没什么用的。希望这篇文章可以帮助到大家。